using System;
using System.Collections.Generic;
using System.Text;
using System.CodeDom;
using System.Data.Common;
using Microsoft.CSharp;
using System.CodeDom.Compiler;
using System.IO;

using NBusiness.Data;
using NBusiness.CodeDom;
using NBusiness.Utilities;
using System.Reflection;
using NBusiness.Templates.Contracts;
using NBusiness.Properties;
using NBusiness.CodeDom.Compiler;
using NBusiness.ESharp.Compiler;
using System.Collections;
using Microsoft.VisualBasic;
using System.Data;
using System.ComponentModel;

using Csla;

namespace NBusiness.Templates.Csla
{
	[Serializable]
    [ResourceDescription("EntityBaseDescription")]
	public class BusinessBaseTemplate : ITemplate, IEntityBase, IParentRelationship
	{

        #region Constructor
		public BusinessBaseTemplate()
			: base()
		{
        }
		#endregion

        #region Generate
        public EntityResourceFile[] Generate(PartialLanguage partialLanguage, params Entity[] entities)
        {
            List<EntityResourceFile> files = new List<EntityResourceFile>();
            foreach (Entity entity in entities)
            {
                CodeCompileUnit unit = new CodeCompileUnit();
                CodeNamespace n = new CodeNamespace((entity.Parent as EntityFamily).Fullname);
                string fileName = "EntityBase\\" + entity.Fullname + "." + partialLanguage.ToString().ToLower();

                #region imports
                n.Comments.Add(new CodeCommentStatement(fileName + " - '" + this.GetType().FullName + "'"));
                n.Comments.Add(new CodeCommentStatement("This file was automatically generated by NBusiness"));
                n.Comments.Add(new CodeCommentStatement("on " + DateTime.Now.ToShortDateString() + " at " + DateTime.Now.ToLongTimeString()));

                n.Imports.Add(new CodeNamespaceImport("System"));
                n.Imports.Add(new CodeNamespaceImport("System.Collections.Generic"));
                n.Imports.Add(new CodeNamespaceImport("System.Data.Common"));
                n.Imports.Add(new CodeNamespaceImport("System.Data"));
                n.Imports.Add(new CodeNamespaceImport("System.Data.SqlClient"));
                n.Imports.Add(new CodeNamespaceImport("System.ComponentModel"));

                n.Imports.Add(new CodeNamespaceImport("NBusiness.Data"));
                n.Imports.Add(new CodeNamespaceImport("NBusiness.Utilities"));
                n.Imports.Add(new CodeNamespaceImport("NBusiness.Data.Validation"));

                n.Imports.Add(new CodeNamespaceImport("Csla"));
                n.Imports.Add(new CodeNamespaceImport("Csla.Data"));
                n.Imports.Add(new CodeNamespaceImport("Csla.Validation"));
                unit.Namespaces.Add(n); 
                #endregion

                StringWriter sw = new StringWriter();
                CodeTypeDeclaration d = CreateMainClass(entity);
                if (d != null)
                {
                    n.Types.Add(d);

                    CreateConstructors(entity, d);
                    //CreateFieldNames(entity, d);
                    CreateFieldsAndProperties(entity, d);
                    CreateCriteria(entity, d);
                    CreateFactoryMethods(entity, d);
                    CreateDataPortalMethods(entity, d);
                    CreateRules(entity, d);
                    CreateDataAccessMethods(entity, d);
                    CreateRelationships(entity, d);
                    CreateInfoObjectMethods(entity, d);
                    CreateActions(entity, d);
                    CreateGetChildTypes(entity, d);
                    GenerateObjectOverrides(entity, d);

                    CSharpCodeProvider cscp = new CSharpCodeProvider();
                    CodeGeneratorOptions op = new CodeGeneratorOptions();
                    CodeDomProvider provider = null;
                    switch (partialLanguage)
                    {
                        case PartialLanguage.CS:
                            provider = new CSharpCodeProvider();
                            op.BracingStyle = "C";
                            op.ElseOnClosing = false;
                            break;
                        case PartialLanguage.VB:
                            provider = new VBCodeProvider();
                            break;
                    }
                    op.BlankLinesBetweenMembers = false;
                    op.IndentString = "\t";
                    op.VerbatimOrder = true;
                    provider.GenerateCodeFromCompileUnit(unit, sw, op);
                }

                string code = sw.GetStringBuilder().ToString();
                byte[] data = Encoding.UTF8.GetBytes(code);

                EntityResourceFile file = new EntityResourceFile(
                    fileName,
                    EntityBuildAction.Compile);
                file.Data = data;

                files.Add(file);
            }
            return files.ToArray();
        }
        #endregion

		#region Create Main Class
		protected virtual CodeTypeDeclaration CreateMainClass(Entity entity)
		{
			CodeTypeDeclaration d = new CodeTypeDeclaration(entity.Name);
            //d.BaseTypes.Add(new CodeTypeReference(typeof(EntityBase)));
			//d.BaseTypes.Add(new CodeTypeReference(typeof(BusinessBase<Entity>)));
            d.BaseTypes.Add("BusinessBase<" + entity.Name + ">");
			d.IsClass = true;
			d.IsPartial = true;
			d.Attributes = MemberAttributes.Public;
            d.CustomAttributes.Add(new CodeAttributeDeclaration(new CodeTypeReference(typeof(SerializableAttribute))));

			return d;
		} 
		#endregion

		#region Create Constructors
		protected virtual void CreateConstructors(Entity entity, CodeTypeDeclaration declaration)
		{
            CreatePrivateConstructor(entity, declaration);
            //CreateStaticConstructor(entity, declaration);
            //CreateParameterlessConstructor(entity, declaration);
            //CreatePrimaryFetchConstructor(entity, declaration);
            //CreateLoadFromReaderConstructor(entity, declaration);
            //CreateCriteriaConstructors(entity, declaration);
        }
        #region Create Static Constructor
        protected virtual void CreatePrivateConstructor(Entity entity, CodeTypeDeclaration declaration)
        {
            CodeTypeConstructor c = new CodeTypeConstructor();
            c.StartDirectives.Add(new CodeRegionDirective(CodeRegionMode.Start, "Private Constructor"));
            c.Name = entity.Name + "t";
            c.Attributes = MemberAttributes.Private;

            //TODO:
            //_resources = ProjectResources.NewProjectResources();
            //_resources.ListChanged += new System.ComponentModel.ListChangedEventHandler(_resources_ListChanged);
            c.EndDirectives.Add(new CodeRegionDirective(CodeRegionMode.End, ""));
            declaration.Members.Add(c);
        }
        #endregion
        #region Create Static Constructor
        protected virtual void CreateStaticConstructor(Entity entity, CodeTypeDeclaration declaration)
        {   
            CodeTypeConstructor c = new CodeTypeConstructor();
            c.StartDirectives.Add(new CodeRegionDirective(CodeRegionMode.Start, "Constructors"));
            c.Name = entity.Name;
            c.Attributes = MemberAttributes.Public | MemberAttributes.Static;
            
            CodeMethodInvokeExpression initializeAuthorizationRules = new CodeMethodInvokeExpression(
                new CodeTypeReferenceExpression(entity.Name),
                "InitializeAuthorizationRules");
            CodeMethodInvokeExpression initializeValidationRules = new CodeMethodInvokeExpression(
                new CodeTypeReferenceExpression(entity.Name),
                "InitializeValidationRules");
            CodeMethodInvokeExpression initializeAccessRules = new CodeMethodInvokeExpression(
                new CodeTypeReferenceExpression(entity.Name),
                "InitializeAccessRules");

            c.Statements.Add(initializeAuthorizationRules);
            c.Statements.Add(initializeValidationRules);
            c.Statements.Add(initializeAccessRules);

            declaration.Members.Add(c);
        }
        #endregion
        #region Create Parameterless Constructor
        protected virtual void CreateParameterlessConstructor(Entity entity, CodeTypeDeclaration declaration)
        {
            CodeConstructor c = new CodeConstructor();
            c.Name = entity.Name;
            c.Attributes = MemberAttributes.Public;
            c.BaseConstructorArgs.Add(new CodeSnippetExpression(""));

            declaration.Members.Add(c);
        }
        #endregion
		#region Create Primary Fetch Constructor
		protected virtual void CreatePrimaryFetchConstructor(Entity entity, CodeTypeDeclaration declaration)
		{
			CodeConstructor c1 = new CodeConstructor();
            CodeConstructor c2 = new CodeConstructor();

			c1.Name = entity.Name;
			c1.Attributes = MemberAttributes.Assembly;
            c2.Name = entity.Name;
            c2.Attributes = MemberAttributes.Assembly;

            c2.Parameters.Add(new CodeParameterDeclarationExpression(typeof(DbConnection), "cn"));
            c2.BaseConstructorArgs.Add(new CodeVariableReferenceExpression("cn"));

            CodeObjectCreateExpression create = new CodeObjectCreateExpression(
                entity.Name + "Criteria");
                c1.BaseConstructorArgs.Add(create);
                c2.BaseConstructorArgs.Add(create);

            foreach (EntityField f in entity.Fields)
            {
                if (f.IsId)
                {
                    CodeParameterDeclarationExpression p = new CodeParameterDeclarationExpression(
                        f.Type,
                        f.Name);
                    c1.Parameters.Add(p);
                    c2.Parameters.Add(p);

                    create.Parameters.Add(new CodeVariableReferenceExpression(f.Name));
                }
            }

            declaration.Members.Add(c1);
            declaration.Members.Add(c2);
        }
        #endregion
        #region Create Load From Reader Constructor
        private void CreateLoadFromReaderConstructor(Entity entity, CodeTypeDeclaration declaration)
        {
            CodeConstructor c = new CodeConstructor();
            c.Name = entity.Name;
            c.Attributes = MemberAttributes.Assembly;
            c.Parameters.Add(new CodeParameterDeclarationExpression(typeof(DataReader), "dr"));
            c.BaseConstructorArgs.Add(new CodeVariableReferenceExpression("dr"));
            c.Parameters.Add(new CodeParameterDeclarationExpression(typeof(NBusiness.Data.CriteriaBase), "c"));
            c.BaseConstructorArgs.Add(new CodeVariableReferenceExpression("c"));
            declaration.Members.Add(c);
        }
        #endregion
        #region Create Criteria Constructors
        protected virtual void CreateCriteriaConstructors(Entity entity, CodeTypeDeclaration declaration)
        {
            CodeConstructor c1 = new CodeConstructor();
            CodeConstructor c2 = new CodeConstructor();

            c1.Name = entity.Name;
            c1.Attributes = MemberAttributes.Assembly;
            c2.Name = entity.Name;
            c2.Attributes = MemberAttributes.Assembly;

            c1.Parameters.Add(new CodeParameterDeclarationExpression(typeof(NBusiness.Data.CriteriaBase), "criteria"));
            c1.BaseConstructorArgs.Add(new CodeVariableReferenceExpression("criteria"));

            c2.Parameters.Add(new CodeParameterDeclarationExpression(typeof(DbConnection), "cn"));
            c2.Parameters.Add(new CodeParameterDeclarationExpression(typeof(NBusiness.Data.CriteriaBase), "criteria"));
            c2.BaseConstructorArgs.Add(new CodeVariableReferenceExpression("cn"));
            c2.BaseConstructorArgs.Add(new CodeVariableReferenceExpression("criteria"));
            c2.EndDirectives.Add(new CodeRegionDirective(CodeRegionMode.End, ""));

            declaration.Members.Add(c1);
            declaration.Members.Add(c2);
        } 
        #endregion
        #endregion

        #region Create Field Names
        private void CreateFieldNames(Entity entity, CodeTypeDeclaration declaration)
        {
            CodeTypeDeclaration d = new CodeTypeDeclaration("Fields");
            d.IsClass = true;
            d.TypeAttributes = TypeAttributes.NestedPublic | TypeAttributes.Sealed;
            d.StartDirectives.Add(new CodeRegionDirective(CodeRegionMode.Start, "Field Names"));
            d.EndDirectives.Add(new CodeRegionDirective(CodeRegionMode.End, ""));

            foreach (EntityField f in entity.Fields)
            {
                CodeMemberField field = new CodeMemberField(typeof(string), f.Name);
                field.Attributes = MemberAttributes.Public | MemberAttributes.Const;
                field.InitExpression = new CodePrimitiveExpression(f.Name);
                d.Members.Add(field);
            }

            // Create a property that has each of the field names in an array.
            CodeMemberProperty p = new CodeMemberProperty();
            p.Type = new CodeTypeReference(typeof(string[]));
            p.Name = "Names";
            p.Attributes = MemberAttributes.Public | MemberAttributes.Static;

            CodeObjectCreateExpression fieldNamesInit = new CodeObjectCreateExpression(
                typeof(List<string>));

            CodeVariableDeclarationStatement fieldNamesDeclaration =
                new CodeVariableDeclarationStatement(
                typeof(List<string>), 
                "nameArray",
                fieldNamesInit);
            p.GetStatements.Add(fieldNamesDeclaration);

            CodeVariableReferenceExpression names = new CodeVariableReferenceExpression("nameArray");
            foreach (EntityField f in entity.Fields)
            {                
                CodeMethodReferenceExpression add = new CodeMethodReferenceExpression(
                    names,
                    "Add");
                CodeMethodInvokeExpression addName = new CodeMethodInvokeExpression(
                    add,
                    new CodePrimitiveExpression(f.Name));

                p.GetStatements.Add(addName);
            }

            CodeMethodReferenceExpression toArray = new CodeMethodReferenceExpression(
                names,
                "ToArray");
            CodeMethodInvokeExpression toArrayInvoke = new CodeMethodInvokeExpression(toArray);
            CodeMethodReturnStatement returnArray = new CodeMethodReturnStatement(toArrayInvoke);

            p.GetStatements.Add(returnArray);
            d.Members.Add(p);
            declaration.Members.Add(d);
        }
        #endregion

        #region Create Fields and Properties
        /// <summary>
        /// Creates the fields and properties.
        /// </summary>
        /// <param name="entity">The entity.</param>
        /// <param name="declaration">The declaration.</param>
        protected virtual void CreateFieldsAndProperties(Entity entity, CodeTypeDeclaration declaration)
		{
            int index = declaration.Members.Count;
            foreach (EntityField field in entity.Fields)
            {
                #region create the field
                string fieldName = string.Format("_{0}", field.Name.ToLower());
                CodeMemberField f = new CodeMemberField(field.Type, fieldName);
                f.Attributes = MemberAttributes.Private; 
                #endregion

                #region create the property
                CodeMemberProperty p = new CodeMemberProperty();
                p.Name = field.ToString();
                p.Attributes = MemberAttributes.Public | MemberAttributes.Final;
                p.Type = new CodeTypeReference(field.Type);

                EntityRelationship relationship = null;
                foreach (EntityRelationship rel in entity.Relationships)
                {
                    if (rel.Type == EntityRelationshipType.Parent &&
                        rel.From == entity && rel.On.ContainsKey(field) &&
                        Dependency.GetContract<IParentRelationship>(rel.With) != null)
                    {
                        relationship = rel;
                        break;
                    }
                } 
                #endregion

                #region property getter
                p.GetStatements.Add(new CodeMethodInvokeExpression(null, "CanReadProperty", new CodePrimitiveExpression(true)));

                p.GetStatements.Add(new CodeMethodReturnStatement(
                    new CodeFieldReferenceExpression(
                        new CodeThisReferenceExpression(),
                        fieldName))); 
                #endregion

                #region property setter
                if (!field.IsReadOnly)
                {
                    #region Set Field
                    if (!field.IsId && relationship == null)
                    {
                        p.SetStatements.Add(new CodeMethodInvokeExpression(null, "CanWriteProperty", new CodePrimitiveExpression(true)));

                        CodeVariableReferenceExpression valueReference = new CodeVariableReferenceExpression("value");
                        CodeFieldReferenceExpression fieldReference = new CodeFieldReferenceExpression(null, fieldName);

                        CodeAssignStatement valueAssign = new CodeAssignStatement(
                            valueReference,
                            new CodePrimitiveExpression(string.Empty));

                        CodeAssignStatement fieldAssign = new CodeAssignStatement(
                            fieldReference, valueReference);

                        CodeBinaryOperatorExpression valueIsNull = new CodeBinaryOperatorExpression(valueReference, CodeBinaryOperatorType.IdentityEquality, new CodePrimitiveExpression(null));
                        CodeConditionStatement ifValueIsNull = new CodeConditionStatement(valueIsNull, valueAssign);

                        p.SetStatements.Add(ifValueIsNull);

                        CodeBinaryOperatorExpression valueIsDirty = new CodeBinaryOperatorExpression(fieldReference, CodeBinaryOperatorType.IdentityInequality, valueReference);
                        CodeConditionStatement ifValueIsDirty = new CodeConditionStatement(valueIsDirty, fieldAssign);
                        ifValueIsDirty.TrueStatements.Add(new CodeMethodInvokeExpression(null, "PropertyHasChanged"));

                        p.SetStatements.Add(ifValueIsDirty);

                    }
                    #endregion
                    #region Set ID Field
                    else if (!field.IsAuto && relationship == null)
                    {
                        CodeConditionStatement c = new CodeConditionStatement(
                            new CodePropertyReferenceExpression(
                                null,
                                "IsNew"));

                        c.TrueStatements.Add(
                            new CodeMethodInvokeExpression(
                                null,
                                "VerifyCanSet",
                                new CodePrimitiveExpression(field.Name)));

                        c.TrueStatements.Add(
                            new CodeAssignStatement(
                                new CodeFieldReferenceExpression(
                                    null,
                                    "_" + field.Name),
                                new CodePropertySetValueReferenceExpression()));

                        c.TrueStatements.Add(
                            new CodeMethodInvokeExpression(null, "MarkDirty"));

                        c.TrueStatements.Add(new CodeMethodInvokeExpression(
                            null,
                            "OnPropertyChanged",
                            new CodePrimitiveExpression(field.Name)));

                        p.SetStatements.Add(c);
                    }
                    #endregion
                    #region Set Child Field
                    else if (relationship != null)
                    {
                        p.SetStatements.Add(
                            new CodeMethodInvokeExpression(
                                null,
                                "VerifyCanSet",
                                new CodePrimitiveExpression(field.Name)));

                        p.SetStatements.Add(
                            new CodeAssignStatement(
                                new CodePropertyReferenceExpression(
                                    null,
                                    relationship.Name),
                                new CodePrimitiveExpression(null)));

                        p.SetStatements.Add(
                            new CodeAssignStatement(
                                new CodeFieldReferenceExpression(
                                    null,
                                    "_" + field.Name),
                                new CodePropertySetValueReferenceExpression()));

                        p.SetStatements.Add(new CodeMethodInvokeExpression(null, "MarkDirty"));

                        p.SetStatements.Add(new CodeMethodInvokeExpression(
                            null,
                            "OnPropertyChanged",
                            new CodePrimitiveExpression(field.Name)));
                    }
                    #endregion
                }  
                #endregion

                declaration.Members.Add(f);
                declaration.Members.Add(p);
            }

            #region region declaration
            if (entity.Fields.Count > 0)
            {
                declaration.Members[index].StartDirectives.Add(
                    new CodeRegionDirective(CodeRegionMode.Start, "Member Fields and Properties"));

                index += (entity.Fields.Count * 2);
                declaration.Members[index - 1].EndDirectives.Add(
                    new CodeRegionDirective(CodeRegionMode.End, ""));
            } 
            #endregion
		} 
		#endregion

		#region Create Criteria
		protected virtual void CreateCriteria(Entity entity, CodeTypeDeclaration declaration)
		{
            CodeTypeDeclaration d = new CodeTypeDeclaration("Criteria");
			d.IsClass = true;
            d.Attributes = MemberAttributes.Public;
            d.CustomAttributes.Add(new CodeAttributeDeclaration(new CodeTypeReference(typeof(SerializableAttribute))));
            d.StartDirectives.Add(new CodeRegionDirective(CodeRegionMode.Start, "Criteria"));
            d.EndDirectives.Add(new CodeRegionDirective(CodeRegionMode.End, ""));

			CodeConstructor c1 = new CodeConstructor();
			c1.Attributes = MemberAttributes.Public;
			d.Members.Add(c1);

			foreach (EntityField f in entity.Fields)
			{
				if (f.IsId)
				{
                    CodeParameterDeclarationExpression p1 = new CodeParameterDeclarationExpression(f.Type, f.Name);
                    CodeAssignStatement assign = new CodeAssignStatement(
                        new CodeFieldReferenceExpression(null, "_" + f.Name),
                        new CodeVariableReferenceExpression(f.Name));

					c1.Parameters.Add(p1);
                    c1.Statements.Add(assign);

					CodeMemberField mf = new CodeMemberField(f.Type, string.Format("_{0}", f.ToString()));
					mf.Attributes = MemberAttributes.Private;
					d.Members.Add(mf);

					CodeMemberProperty mp = new CodeMemberProperty();
					mp.Name = f.ToString();
					mp.Attributes = MemberAttributes.Public;
					mp.Type = new CodeTypeReference(f.Type);
					mp.HasGet = true;
					mp.HasSet = false;
					mp.GetStatements.Add(
                        new CodeMethodReturnStatement(
                            new CodeFieldReferenceExpression(null, "_" + f.Name)));

					d.Members.Add(mp);
				}
			}

            declaration.Members.Add(d);
		} 
		#endregion

		#region Create Factory Methods
		protected virtual void CreateFactoryMethods(Entity entity, CodeTypeDeclaration declaration)
		{
			CreateNew(entity, declaration);
            CreateGet(entity, declaration);
            CreateDelete(entity, declaration);
            CreateSave(entity, declaration);
			CreateFetch(entity, declaration);
		}
		#region Create NewEntity
        /// <summary>
        /// Creates the create new static method.
        /// </summary>
        /// <param name="entity">The entity.</param>
        /// <param name="declaration">The declaration.</param>
        /// <example>
        /// public static Project NewProject()
        /// {
        ///     if ((CanAddObject() == false))
        ///     {
        ///         throw new System.Security.SecurityException("User not authorized to add a project");
        ///     }
        ///     return DataPortal.Create<Project>();
        /// }
        /// </example>
        protected virtual void CreateNew(Entity entity, CodeTypeDeclaration declaration)
		{
            #region Declaration
            CodeMemberMethod m = new CodeMemberMethod();
            m.Name = "New" + entity.Name;
            m.Attributes = MemberAttributes.Public | MemberAttributes.Static;
            m.ReturnType = new CodeTypeReference(entity.Name);
            m.StartDirectives.Add(new CodeRegionDirective(CodeRegionMode.Start, "Factories")); 
            #endregion

            #region !CanAddObject
            //CanAddObject() == false
            CodeBinaryOperatorExpression ifCanAddObject = new CodeBinaryOperatorExpression(
                new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(null, "CanAddObject"), new CodeExpression[0]),
                CodeBinaryOperatorType.ValueEquality,
                new CodePrimitiveExpression(false));

            //throw new System.Security.SecurityException("User not authorized to add a project")
            CodeThrowExceptionStatement throwSecurityException = new CodeThrowExceptionStatement(new CodeObjectCreateExpression(new CodeTypeReference("System.Security.SecurityException"), new CodePrimitiveExpression("User not authorized to add a " + entity.Name)));

            m.Statements.Add(new CodeConditionStatement(ifCanAddObject, throwSecurityException));
            #endregion

            #region Return
            //return DataPortal.Create<Project>();
            CodeMethodReferenceExpression DataPortal_Create = new CodeMethodReferenceExpression(new CodeVariableReferenceExpression("DataPortal"), "Create<" + entity.Name + ">");
            m.Statements.Add(new CodeMethodReturnStatement(new CodeMethodInvokeExpression(DataPortal_Create, new CodeExpression[0])));
            declaration.Members.Add(m); 
            #endregion
		} 
		#endregion

        #region Create GetEntity
        /// <summary>
        /// Creates the get factory method.
        /// </summary>
        /// <param name="entity">The entity.</param>
        /// <param name="declaration">The declaration.</param>
        /// <example>
        ///     public static Project GetProject(Guid id)
		///     {
		///	        if ((CanGetObject() == false))
		///	        {
		///		        throw new System.Security.SecurityException("User not authorized to view a Project");
		///	        }
		///	        return DataPortal.Create<Project>(new Criteria(id));
        ///     }
        /// </example>
		protected virtual void CreateGet(Entity entity, CodeTypeDeclaration declaration)
		{
            #region Declaration
            CodeMemberMethod m = new CodeMemberMethod();
            m.Name = "Get" + entity.Name;
            m.Attributes = MemberAttributes.Public | MemberAttributes.Static;
            m.ReturnType = new CodeTypeReference(entity.Name);
            m.Parameters.Add(new CodeParameterDeclarationExpression(new CodeTypeReference("Guid"), "id"));
            #endregion

            #region !CanGetObject
            //CanGetObject() == false
            CodeBinaryOperatorExpression ifCanGetObject = new CodeBinaryOperatorExpression(
                new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(null, "CanGetObject"), new CodeExpression[]{ }),
                CodeBinaryOperatorType.ValueEquality,
                new CodePrimitiveExpression(false));

            //throw new System.Security.SecurityException("User not authorized to view a project")
            CodeThrowExceptionStatement throwSecurityException = new CodeThrowExceptionStatement(new CodeObjectCreateExpression(new CodeTypeReference("System.Security.SecurityException"), new CodePrimitiveExpression("User not authorized to view a " + entity.Name)));

            m.Statements.Add(new CodeConditionStatement(ifCanGetObject, throwSecurityException));
            #endregion

            #region Return
            CodeObjectCreateExpression newCriteria = new CodeObjectCreateExpression(new CodeTypeReference("Criteria"),new CodeExpression[]{new CodeVariableReferenceExpression("id")});
            //return DataPortal.Fetch<Project>(new Criteria(id));
            CodeMethodReferenceExpression DataPortal_Create = new CodeMethodReferenceExpression(
                new CodeVariableReferenceExpression("DataPortal"),
                "Create<" + entity.Name + ">");
            m.Statements.Add(new CodeMethodReturnStatement(new CodeMethodInvokeExpression(DataPortal_Create, new CodeExpression[] { newCriteria })));
            declaration.Members.Add(m); 
            #endregion
		} 
        #endregion

        #region Create DeleteEntity
        protected virtual void CreateDelete(Entity entity, CodeTypeDeclaration declaration)
        {
            #region Declaration
            CodeMemberMethod m = new CodeMemberMethod();
            m.Name = "Delete" + entity.Name;
            m.Attributes = MemberAttributes.Public | MemberAttributes.Static;
            m.ReturnType = new CodeTypeReference(entity.Name);
            m.Parameters.Add(new CodeParameterDeclarationExpression(new CodeTypeReference("Guid"), "id"));
            #endregion

            #region !CanGetObject
            //CanGetObject() == false
            CodeBinaryOperatorExpression ifCanDeleteObject = new CodeBinaryOperatorExpression(
                new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(null, "CanDeleteObject"), new CodeExpression[] { }),
                CodeBinaryOperatorType.ValueEquality,
                new CodePrimitiveExpression(false));

            //throw new System.Security.SecurityException("User not authorized to view a project")
            CodeThrowExceptionStatement throwSecurityException = new CodeThrowExceptionStatement(new CodeObjectCreateExpression(new CodeTypeReference("System.Security.SecurityException"), new CodePrimitiveExpression("User not authorized to delete a " + entity.Name)));

            m.Statements.Add(new CodeConditionStatement(ifCanDeleteObject, throwSecurityException));
            #endregion

            #region Return
            CodeObjectCreateExpression newCriteria = new CodeObjectCreateExpression(new CodeTypeReference("Criteria"), new CodeExpression[] { new CodeVariableReferenceExpression("id") });
            //return DataPortal.Fetch<Project>(new Criteria(id));
            CodeMethodReferenceExpression DataPortal_Create = new CodeMethodReferenceExpression(
                new CodeVariableReferenceExpression("DataPortal"),
                "Delete<" + entity.Name + ">");
            m.Statements.Add(new CodeMethodReturnStatement(new CodeMethodInvokeExpression(DataPortal_Create, new CodeExpression[] { newCriteria })));
            declaration.Members.Add(m);
            #endregion
        }  
        #endregion

        #region Create Save Override
        /// <summary>
        /// Creates the save override method.
        /// </summary>
        /// <param name="entity">The entity.</param>
        /// <param name="declaration">The declaration.</param>
        /// <example>
        /// public override void Save()
        /// {
        ///    if ((IsDeleted
        ///                && (CanDeleteObject() == false)))
        ///    {
        ///        throw new System.Security.SecurityException("User Not authorized to remove a project");
        ///    }
        ///    if ((IsNew
        ///                && (CanAddObject() == false)))
        ///    {
        ///        throw new System.Security.SecurityException("User Not authorized to add a project");
        ///    }
        ///    if ((CanEditObject() == false))
        ///    {
        ///        throw new System.Security.SecurityException("User Not authorized to update a project");
        ///    }
        ///    return base.Save();
        /// }
        /// </example>
        protected virtual void CreateSave(Entity entity, CodeTypeDeclaration declaration)
        {
            #region Declaration
            CodeMemberMethod m = new CodeMemberMethod();
            m.Attributes = MemberAttributes.Override | MemberAttributes.Public;
            m.Name = "Save";
            #endregion

            #region Logic
            #region IsDeleted
            CodeBinaryOperatorExpression hasNoDeleteRights = new CodeBinaryOperatorExpression(
                new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(null, "CanDeleteObject"), new CodeExpression[0]),
                CodeBinaryOperatorType.ValueEquality,
                new CodePrimitiveExpression(false));

            CodeBinaryOperatorExpression isDeletedAndHasNoDeleteRights = new CodeBinaryOperatorExpression(
                new CodeVariableReferenceExpression("IsDeleted"),
                CodeBinaryOperatorType.BooleanAnd,
                hasNoDeleteRights);

            CodeObjectCreateExpression deleteSecurityException = new CodeObjectCreateExpression(
                new CodeTypeReference("System.Security.SecurityException"),
                new CodePrimitiveExpression("User Not authorized to remove a project"));

            m.Statements.Add(new CodeConditionStatement(isDeletedAndHasNoDeleteRights, new CodeThrowExceptionStatement(deleteSecurityException)));
            #endregion
            #region IsNew
            CodeBinaryOperatorExpression hasNoAddRights = new CodeBinaryOperatorExpression(
                new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(null, "CanAddObject"), new CodeExpression[0]),
                CodeBinaryOperatorType.ValueEquality,
                new CodePrimitiveExpression(false));

            CodeBinaryOperatorExpression isNewAndHasNoAddRights = new CodeBinaryOperatorExpression(
                new CodeVariableReferenceExpression("IsNew"),
                CodeBinaryOperatorType.BooleanAnd,
                hasNoAddRights);

            CodeObjectCreateExpression addSecurityException = new CodeObjectCreateExpression(
                new CodeTypeReference("System.Security.SecurityException"),
                new CodePrimitiveExpression("User Not authorized to add a project"));

            m.Statements.Add(new CodeConditionStatement(isNewAndHasNoAddRights, new CodeThrowExceptionStatement(addSecurityException)));
            #endregion
            #region IsUpdated
            CodeBinaryOperatorExpression hasNoEditRights = new CodeBinaryOperatorExpression(
                new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(null, "CanEditObject"), new CodeExpression[0]),
                CodeBinaryOperatorType.ValueEquality,
                new CodePrimitiveExpression(false));

            CodeObjectCreateExpression updateSecurityException = new CodeObjectCreateExpression(
                new CodeTypeReference("System.Security.SecurityException"),
                new CodePrimitiveExpression("User Not authorized to update a project"));

            m.Statements.Add(new CodeConditionStatement(hasNoEditRights, new CodeThrowExceptionStatement(updateSecurityException)));
            #endregion
            #endregion

            #region Return
            //return base.Save();
            m.Statements.Add(new CodeMethodReturnStatement(
                new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(new CodeBaseReferenceExpression(), "Save"),
                new CodeExpression[0])));
            declaration.Members.Add(m);
            #endregion
        }
        #endregion

        #region Create Fetch{}
        protected virtual void CreateFetch(Entity entity, CodeTypeDeclaration declaration)
        {
            CodeMemberMethod m1 = new CodeMemberMethod();
            m1.Name = "Fetch";
            m1.Attributes = MemberAttributes.Public | MemberAttributes.Static;
            m1.ReturnType = new CodeTypeReference(entity.Name);

            CodeMemberMethod m2 = new CodeMemberMethod();
            m2.Name = "Fetch";
            m2.Attributes = MemberAttributes.Assembly | MemberAttributes.Static;
            m2.ReturnType = new CodeTypeReference(entity.Name);

            CodeMemberMethod m3 = new CodeMemberMethod();
            m3.Name = "Fetch";
            m3.Attributes = MemberAttributes.Assembly | MemberAttributes.Static;
            m3.ReturnType = new CodeTypeReference(entity.Name);

            CodeObjectCreateExpression criteria = new CodeObjectCreateExpression(
                entity.Name + "Criteria");

            
            CodeMethodReturnStatement returnCriteria = new CodeMethodReturnStatement(
                    new CodeObjectCreateExpression(
                        entity.Name,
                        criteria));

            foreach(string idName in EntityField.GetIdNames(entity.Fields.ToArray()))
            {
                criteria.Parameters.Add(new CodeVariableReferenceExpression(idName));
            }

            m1.Statements.Add(returnCriteria);
    
            m2.Parameters.Add(new CodeParameterDeclarationExpression(typeof(DbConnection), "cn"));
            
            CodeMethodReturnStatement returnCriteria2 = new CodeMethodReturnStatement(
                    new CodeObjectCreateExpression(
                        entity.Name,
                        new CodeVariableReferenceExpression("cn"),
                        criteria));

            m2.Statements.Add(returnCriteria2);
                
            m3.Parameters.Add(new CodeParameterDeclarationExpression(typeof(DataReader), "dr"));
            m3.Parameters.Add(new CodeParameterDeclarationExpression(typeof(NBusiness.Data.CriteriaBase), "c"));
            m3.Statements.Add(new CodeMethodReturnStatement(
                new CodeObjectCreateExpression(
                    entity.Name,
                    new CodeVariableReferenceExpression("dr"),
                    new CodeVariableReferenceExpression("c"))));
            m3.EndDirectives.Add(new CodeRegionDirective(CodeRegionMode.End, ""));

            foreach (EntityField f in entity.Fields)
            {
                if (f.IsId)
                {
                    CodeParameterDeclarationExpression p = new CodeParameterDeclarationExpression(f.Type, f.Name);
                    m1.Parameters.Add(p);
                    m2.Parameters.Add(p);
                }
            }
            declaration.Members.Add(m1);
            declaration.Members.Add(m2);
            declaration.Members.Add(m3);
        }
        #endregion
        #endregion

        #region Create DataPortal Methods
        protected virtual void CreateDataPortalMethods(Entity entity, CodeTypeDeclaration declaration)
        {
            CreateDataPortal_Create(entity, declaration);
            CreateDataPortal_Fetch(entity, declaration);
        }

        #region Create DataPortal_Create
        /// <summary>
        /// Creates the DataPortal_Create method
        /// </summary>
        /// <param name="entity">The entity.</param>
        /// <param name="declaration">The declaration.</param>
        /// <example>
        /// [Csla.RunLocalAttribute()]
        /// protected override void DataPortal_Create()
        /// {
        ///    _id = Guid.NewGuid();
        ///    ValidationRules.CheckRules();
        /// }
        /// </example>
        protected virtual void CreateDataPortal_Create(Entity entity, CodeTypeDeclaration declaration)
        {
            CodeMemberMethod m = new CodeMemberMethod();
            m.Attributes = MemberAttributes.Override | MemberAttributes.Assembly | MemberAttributes.FamilyAndAssembly | MemberAttributes.Family;
            m.Name = "DataPortal_Create";
            m.CustomAttributes.Add(new CodeAttributeDeclaration(new CodeTypeReference(typeof(RunLocalAttribute))));

            CodeAssignStatement assignIdToNewGuid = new CodeAssignStatement(
                new CodeFieldReferenceExpression(null, "_id"),
                new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(new CodeVariableReferenceExpression("Guid"), "NewGuid")));
            m.Statements.Add(assignIdToNewGuid);

            CodeExpressionStatement callCheckRules = new CodeExpressionStatement(
                new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(new CodeVariableReferenceExpression("ValidationRules"), "CheckRules")));
            m.Statements.Add(callCheckRules);

            declaration.Members.Add(m);
        }
        #endregion
        #region Create DataPortal_Fetch
        /// <summary>
        /// Creates the DataPortal_Fetch method
        /// </summary>
        /// <param name="entity">The entity.</param>
        /// <param name="declaration">The declaration.</param>
        /// <example>
        /// [Csla.RunLocalAttribute()]
        /// protected override void DataPortal_Create()
        /// {
        ///    _id = Guid.NewGuid();
        ///    ValidationRules.CheckRules();
        /// }
        /// </example>
        protected virtual void CreateDataPortal_Fetch(Entity entity, CodeTypeDeclaration declaration)
        {
            CodeMemberMethod m = new CodeMemberMethod();
            m.Attributes = MemberAttributes.Assembly | MemberAttributes.FamilyOrAssembly | MemberAttributes.Private;
            m.Name = "DataPortal_Fetch";
            m.Parameters.Add(new CodeParameterDeclarationExpression(new CodeTypeReference("Criteria"), "criteria"));

            CodeVariableDeclarationStatement declareSqlConnection = new CodeVariableDeclarationStatement(new CodeTypeReference("SqlConnection"), "cn", new CodeObjectCreateExpression(new CodeTypeReference("SqlConnection"), new CodePrimitiveExpression("ConnectionString")));
            m.Statements.Add(declareSqlConnection);

            CodeTryCatchFinallyStatement _try1 = new CodeTryCatchFinallyStatement();
            CodeExpressionStatement _expr5 = new CodeExpressionStatement();
            CodeMethodInvokeExpression _invoke5 = new CodeMethodInvokeExpression();
            CodeMethodReferenceExpression _Open_method1 = new CodeMethodReferenceExpression();
            _Open_method1.MethodName = "Open";
            CodeVariableReferenceExpression _arg5 = new CodeVariableReferenceExpression();
            _arg5.VariableName = "cn";
            _Open_method1.TargetObject = _arg5;
            _invoke5.Method = _Open_method1;
            _expr5.Expression = _invoke5;
            _try1.TryStatements.Add(_expr5);

            CodeVariableDeclarationStatement _decl2 = new CodeVariableDeclarationStatement();
            CodeMethodInvokeExpression _invoke6 = new CodeMethodInvokeExpression();
            CodeMethodReferenceExpression _CreateCommand_method1 = new CodeMethodReferenceExpression();
            _CreateCommand_method1.MethodName = "CreateCommand";
            CodeVariableReferenceExpression _arg6 = new CodeVariableReferenceExpression();
            _arg6.VariableName = "cn";
            _CreateCommand_method1.TargetObject = _arg6;
            _invoke6.Method = _CreateCommand_method1;
            _decl2.InitExpression = _invoke6;
            _decl2.Name = "cm";
            CodeTypeReference _SqlCommand_type1 = new CodeTypeReference("SqlCommand");
            _decl2.Type = _SqlCommand_type1;
            _try1.TryStatements.Add(_decl2);

            CodeTryCatchFinallyStatement _try2 = new CodeTryCatchFinallyStatement();
            CodeAssignStatement _assign3 = new CodeAssignStatement();
            CodePropertyReferenceExpression _prop3 = new CodePropertyReferenceExpression();
            _prop3.PropertyName = "CommandType";
            CodeVariableReferenceExpression _arg7 = new CodeVariableReferenceExpression();
            _arg7.VariableName = "cm";
            _prop3.TargetObject = _arg7;
            _assign3.Left = _prop3;
            CodePropertyReferenceExpression _prop4 = new CodePropertyReferenceExpression();
            _prop4.PropertyName = "StoredProcedure";
            CodeVariableReferenceExpression _arg8 = new CodeVariableReferenceExpression();
            _arg8.VariableName = "CommandType";
            _prop4.TargetObject = _arg8;
            _assign3.Right = _prop4;
            _try2.TryStatements.Add(_assign3);

            CodeAssignStatement _assign4 = new CodeAssignStatement();
            CodePropertyReferenceExpression _prop5 = new CodePropertyReferenceExpression();
            _prop5.PropertyName = "CommandText";
            CodeVariableReferenceExpression _arg9 = new CodeVariableReferenceExpression();
            _arg9.VariableName = "cm";
            _prop5.TargetObject = _arg9;
            _assign4.Left = _prop5;
            CodePrimitiveExpression _value6 = new CodePrimitiveExpression();
            _value6.Value = "getProject";
            _assign4.Right = _value6;
            _try2.TryStatements.Add(_assign4);

            CodeExpressionStatement _expr6 = new CodeExpressionStatement();
            CodeMethodInvokeExpression _invoke7 = new CodeMethodInvokeExpression();
            CodePrimitiveExpression _value7 = new CodePrimitiveExpression();
            _value7.Value = "@id";
            _invoke7.Parameters.Add(_value7);

            CodePropertyReferenceExpression _prop6 = new CodePropertyReferenceExpression();
            _prop6.PropertyName = "Id";
            CodeVariableReferenceExpression _arg10 = new CodeVariableReferenceExpression();
            _arg10.VariableName = "criteria";
            _prop6.TargetObject = _arg10;
            _invoke7.Parameters.Add(_prop6);

            CodeMethodReferenceExpression _AddWithValue_method1 = new CodeMethodReferenceExpression();
            _AddWithValue_method1.MethodName = "AddWithValue";
            CodePropertyReferenceExpression _prop7 = new CodePropertyReferenceExpression();
            _prop7.PropertyName = "Parameters";
            CodeVariableReferenceExpression _arg11 = new CodeVariableReferenceExpression();
            _arg11.VariableName = "cm";
            _prop7.TargetObject = _arg11;
            _AddWithValue_method1.TargetObject = _prop7;
            _invoke7.Method = _AddWithValue_method1;
            _expr6.Expression = _invoke7;
            _try2.TryStatements.Add(_expr6);

            CodeVariableDeclarationStatement _decl3 = new CodeVariableDeclarationStatement();
            CodeObjectCreateExpression _new2 = new CodeObjectCreateExpression();
            CodeTypeReference _SafeDataReader_type1 = new CodeTypeReference("SafeDataReader");
            _new2.CreateType = _SafeDataReader_type1;
            CodeMethodInvokeExpression _invoke8 = new CodeMethodInvokeExpression();
            CodeMethodReferenceExpression _ExecuteReader_method1 = new CodeMethodReferenceExpression();
            _ExecuteReader_method1.MethodName = "ExecuteReader";
            CodeVariableReferenceExpression _arg12 = new CodeVariableReferenceExpression();
            _arg12.VariableName = "cm";
            _ExecuteReader_method1.TargetObject = _arg12;
            _invoke8.Method = _ExecuteReader_method1;
            _new2.Parameters.Add(_invoke8);

            _decl3.InitExpression = _new2;
            _decl3.Name = "dr";
            CodeTypeReference _SafeDataReader_type2 = new CodeTypeReference("SafeDataReader");
            _decl3.Type = _SafeDataReader_type2;
            _try2.TryStatements.Add(_decl3);

            CodeTryCatchFinallyStatement _try3 = new CodeTryCatchFinallyStatement();
            CodeExpressionStatement _expr7 = new CodeExpressionStatement();
            CodeMethodInvokeExpression _invoke9 = new CodeMethodInvokeExpression();
            CodeMethodReferenceExpression _Read_method1 = new CodeMethodReferenceExpression();
            _Read_method1.MethodName = "Read";
            CodeVariableReferenceExpression _arg13 = new CodeVariableReferenceExpression();
            _arg13.VariableName = "dr";
            _Read_method1.TargetObject = _arg13;
            _invoke9.Method = _Read_method1;
            _expr7.Expression = _invoke9;
            _try3.TryStatements.Add(_expr7);

            CodeAssignStatement _assign5 = new CodeAssignStatement();
            CodeFieldReferenceExpression _field6 = new CodeFieldReferenceExpression();
            _field6.FieldName = "_id";
            CodeThisReferenceExpression _this10 = new CodeThisReferenceExpression();
            _field6.TargetObject = _this10;
            _assign5.Left = _field6;
            CodeMethodInvokeExpression _invoke10 = new CodeMethodInvokeExpression();
            CodePrimitiveExpression _value8 = new CodePrimitiveExpression();
            _value8.Value = "Id";
            _invoke10.Parameters.Add(_value8);

            CodeMethodReferenceExpression _GetGuid_method1 = new CodeMethodReferenceExpression();
            _GetGuid_method1.MethodName = "GetGuid";
            CodeVariableReferenceExpression _arg14 = new CodeVariableReferenceExpression();
            _arg14.VariableName = "dr";
            _GetGuid_method1.TargetObject = _arg14;
            _invoke10.Method = _GetGuid_method1;
            _assign5.Right = _invoke10;
            _try3.TryStatements.Add(_assign5);

            CodeAssignStatement _assign6 = new CodeAssignStatement();
            CodeFieldReferenceExpression _field7 = new CodeFieldReferenceExpression();
            _field7.FieldName = "_name";
            CodeThisReferenceExpression _this11 = new CodeThisReferenceExpression();
            _field7.TargetObject = _this11;
            _assign6.Left = _field7;
            CodeMethodInvokeExpression _invoke11 = new CodeMethodInvokeExpression();
            CodePrimitiveExpression _value9 = new CodePrimitiveExpression();
            _value9.Value = "Name";
            _invoke11.Parameters.Add(_value9);

            CodeMethodReferenceExpression _GetString_method1 = new CodeMethodReferenceExpression();
            _GetString_method1.MethodName = "GetString";
            CodeVariableReferenceExpression _arg15 = new CodeVariableReferenceExpression();
            _arg15.VariableName = "dr";
            _GetString_method1.TargetObject = _arg15;
            _invoke11.Method = _GetString_method1;
            _assign6.Right = _invoke11;
            _try3.TryStatements.Add(_assign6);

            CodeExpressionStatement _expr8 = new CodeExpressionStatement();
            CodeMethodInvokeExpression _invoke12 = new CodeMethodInvokeExpression();
            CodeMethodReferenceExpression _Dispose_method1 = new CodeMethodReferenceExpression();
            _Dispose_method1.MethodName = "Dispose";
            CodeCastExpression _cast1 = new CodeCastExpression();
            CodeVariableReferenceExpression _arg16 = new CodeVariableReferenceExpression();
            _arg16.VariableName = "dr";
            _cast1.Expression = _arg16;
            CodeTypeReference _IDisposable_type1 = new CodeTypeReference("IDisposable");
            _cast1.TargetType = _IDisposable_type1;
            _Dispose_method1.TargetObject = _cast1;
            _invoke12.Method = _Dispose_method1;
            _expr8.Expression = _invoke12;
            _try3.FinallyStatements.Add(_expr8);

            _try2.TryStatements.Add(_try3);

            CodeExpressionStatement _expr9 = new CodeExpressionStatement();
            CodeMethodInvokeExpression _invoke13 = new CodeMethodInvokeExpression();
            CodeMethodReferenceExpression _Dispose_method2 = new CodeMethodReferenceExpression();
            _Dispose_method2.MethodName = "Dispose";
            CodeCastExpression _cast2 = new CodeCastExpression();
            CodeVariableReferenceExpression _arg17 = new CodeVariableReferenceExpression();
            _arg17.VariableName = "cm";
            _cast2.Expression = _arg17;
            CodeTypeReference _IDisposable_type2 = new CodeTypeReference("IDisposable");
            _cast2.TargetType = _IDisposable_type2;
            _Dispose_method2.TargetObject = _cast2;
            _invoke13.Method = _Dispose_method2;
            _expr9.Expression = _invoke13;
            _try2.FinallyStatements.Add(_expr9);

            _try1.TryStatements.Add(_try2);

            CodeExpressionStatement _expr10 = new CodeExpressionStatement();
            CodeMethodInvokeExpression _invoke14 = new CodeMethodInvokeExpression();
            CodeMethodReferenceExpression _Dispose_method3 = new CodeMethodReferenceExpression();
            _Dispose_method3.MethodName = "Dispose";
            CodeCastExpression _cast3 = new CodeCastExpression();
            CodeVariableReferenceExpression _arg18 = new CodeVariableReferenceExpression();
            _arg18.VariableName = "cn";
            _cast3.Expression = _arg18;
            CodeTypeReference _IDisposable_type3 = new CodeTypeReference("IDisposable");
            _cast3.TargetType = _IDisposable_type3;
            _Dispose_method3.TargetObject = _cast3;
            _invoke14.Method = _Dispose_method3;
            _expr10.Expression = _invoke14;
            _try1.FinallyStatements.Add(_expr10);

            m.Statements.Add(_try1);

            declaration.Members.Add(m);
        }
        #endregion
        #endregion


        #region Create Rules
        protected virtual void CreateRules(Entity entity, CodeTypeDeclaration declaration)
		{
			CreateAuthorizationRules(entity, declaration);
            CreateValidationRules(entity, declaration);
            CreateFieldsValidate(entity, declaration);
            CreateAccessRules(entity, declaration);
		}

        #region Create Validation Rules
        private void CreateValidationRules(Entity entity, CodeTypeDeclaration declaration)
        {   
            CodeMemberMethod m = new CodeMemberMethod();
            m.StartDirectives.Add(new CodeRegionDirective(CodeRegionMode.Start, "Validation Rules"));
            m.Attributes = MemberAttributes.Family | MemberAttributes.Static;
            m.Name = "InitializeValidationRules";

            CodePropertyReferenceExpression validationRules = new CodePropertyReferenceExpression(
                null,
                "ValidationRules");

            foreach (EntityRule rule in entity.Rules)
            {
                if (rule is EntityValidation)
                {
                    EntityValidation validationRule = rule as EntityValidation;

                    CodeObjectCreateExpression ruleArg = new CodeObjectCreateExpression(validationRule.Type.Argument);
                    
                    for(int x=0;x<validationRule.Type.ArgumentParameters.Count;x++)
                    {
                        ruleArg.Parameters.Add(new CodePrimitiveExpression(
                            Convert.ChangeType(validationRule.Arguments[x], validationRule.Type.ArgumentParameters[x])));
                    }

                    CodeMethodInvokeExpression add = new CodeMethodInvokeExpression(
                        validationRules,
                        "Add",
                        new CodeObjectCreateExpression(
                            validationRule.Type.As,
                            new CodeTypeOfExpression(entity.Name),
                            new CodePrimitiveExpression(validationRule.Validatable.Name),
                            ruleArg));

                    m.Statements.Add(add);
                }
            }

            declaration.Members.Add(m);
        }
        private void CreateFieldsValidate(Entity entity, CodeTypeDeclaration declaration)
        {
            CodeMemberMethod m = new CodeMemberMethod();
            m.Attributes = MemberAttributes.Family | MemberAttributes.Override;
            m.Name = "ValidateFields";
            m.EndDirectives.Add(new CodeRegionDirective(CodeRegionMode.End, ""));

            foreach (EntityField field in entity.Fields)
            {
                CodeMethodReferenceExpression validateFieldReference = new CodeMethodReferenceExpression(
                    null,
                    "ValidateField");
                CodeMethodInvokeExpression validateField = new CodeMethodInvokeExpression(
                    validateFieldReference,
                    new CodePrimitiveExpression(field.Name),
                    new CodePropertyReferenceExpression(
                        null,
                        field.Name));

                m.Statements.Add(validateField);
            }

            declaration.Members.Add(m);
        }
        #endregion
		#region Create Object Authorization Rules
		protected virtual void CreateAuthorizationRules(Entity entity, CodeTypeDeclaration declaration)
		{
            CodeMemberMethod m = new CodeMemberMethod();
            m.Attributes = MemberAttributes.Family | MemberAttributes.Static;
            m.Name = "InitializeAuthorizationRules";
            m.StartDirectives.Add(new CodeRegionDirective(CodeRegionMode.Start, "Authorization Rules"));
            m.EndDirectives.Add(new CodeRegionDirective(CodeRegionMode.End, ""));

            foreach (EntityRule rule in entity.Rules)
            {
                if (rule is EntityAuthorization)
                {
					EntityAuthorization ar = rule as EntityAuthorization;
                    CodePropertyReferenceExpression authorizationRules = new CodePropertyReferenceExpression(
                        null,
                        "AuthorizationRules");

                    CodeFieldReferenceExpression authorizationRuleType = new CodeFieldReferenceExpression(
                        new CodeTypeReferenceExpression(typeof(AuthorizationRuleType)),
                        ar.Type.ToString());

                    CodeObjectCreateExpression authorizationRule = new CodeObjectCreateExpression(
                        typeof(AuthorizationRule),
                        new CodeTypeOfExpression(entity.Name),
                        authorizationRuleType,
                        new CodePrimitiveExpression(ar.Role),
                        new CodePrimitiveExpression(ar.Allow));

                    CodeMethodInvokeExpression authorizationRulesAdd = new CodeMethodInvokeExpression(
                        authorizationRules,
                        "Add",
                        authorizationRule);

                    m.Statements.Add(authorizationRulesAdd);
                }
            }
            declaration.Members.Add(m);
		}
		#endregion
        #region Create Object Access Rules
        protected virtual void CreateAccessRules(Entity entity, CodeTypeDeclaration declaration)
        {
            CodeMemberMethod m = new CodeMemberMethod();
            m.Attributes = MemberAttributes.Family | MemberAttributes.Static;
            m.Name = "InitializeAccessRules";
            m.StartDirectives.Add(new CodeRegionDirective(CodeRegionMode.Start, "Access Rules"));
            m.EndDirectives.Add(new CodeRegionDirective(CodeRegionMode.End, ""));

            foreach (EntityRule relationship in entity.Rules)
            {
                if (relationship is EntityAccess)
                {
                    EntityAccess ar = relationship as EntityAccess;

                    CodePropertyReferenceExpression accessRules = new CodePropertyReferenceExpression(null, "AccessRules");
                    CodeFieldReferenceExpression accessRuleType = new CodeFieldReferenceExpression(
                        new CodeTypeReferenceExpression(typeof(AccessRuleType)),
                        ar.Type.ToString());

                    CodeMethodInvokeExpression accessRulesAdd = new CodeMethodInvokeExpression(
                        accessRules,
                        "Add",
                        new CodeObjectCreateExpression(
                            new CodeTypeReference(typeof(AccessRule)),
                            new CodeTypeOfExpression(entity.Name),
                            new CodePrimitiveExpression(ar.Field.Name),
                            accessRuleType,
                            new CodePrimitiveExpression(ar.Role),
                            new CodePrimitiveExpression(ar.Allow)));

                    m.Statements.Add(accessRulesAdd);
                }
            }
            declaration.Members.Add(m);
        }
        #endregion
        #endregion

        #region CreateRelationships
        protected virtual void CreateRelationships(Entity entity, CodeTypeDeclaration declaration)
		{
            foreach (EntityRelationship relationship in entity.Relationships)
			{
                if (relationship.From == entity)
                {
                    switch (relationship.Type)
                    {
                        case EntityRelationshipType.Child:
                            {
                                CreateChildRelationship(declaration, relationship);
                                break;
                            }
                        case EntityRelationshipType.Parent:
                            {
                                CreateParentRelationship(declaration, relationship);
                                break;
                            }
                        case EntityRelationshipType.Sibling:
                            {
                                CreateSiblingRelationship(declaration, relationship);
                                break;
                            }
                    }
                }
			}

            CreateInitializeRelationshipEvents(entity, declaration);
            CreatePersistChildren(entity, declaration);
            CreatePersistSiblings(entity, declaration);
            CreatePersistParents(entity, declaration);
            CreateDeleteChildren(entity, declaration);
            CreateDeleteSiblings(entity, declaration);
        }

        #region Create InitializeRelationshipEvents
        protected virtual void CreateInitializeRelationshipEvents(Entity entity, CodeTypeDeclaration declaration)
        {
            CodeMemberMethod m = new CodeMemberMethod();
            m.StartDirectives.Add(new CodeRegionDirective(CodeRegionMode.Start, "Initialize Relationship Events"));
            m.EndDirectives.Add(new CodeRegionDirective(CodeRegionMode.End, ""));
            m.Attributes = MemberAttributes.Family | MemberAttributes.Override;
            m.Name = "InitializeRelationshipEvents";
            declaration.Members.Add(m);

            m.Statements.Add(new CodeAttachEventStatement(
                new CodeThisReferenceExpression(),
                "Persisted",
                new CodeDelegateCreateExpression(
                    new CodeTypeReference(typeof(EventHandler<PersistEventArgs>)),
                    new CodeThisReferenceExpression(),
                    "PersistChildren")));
            m.Statements.Add(new CodeAttachEventStatement(
                new CodeThisReferenceExpression(),
                "Persisted",
                new CodeDelegateCreateExpression(
                    new CodeTypeReference(typeof(EventHandler<PersistEventArgs>)),
                    new CodeThisReferenceExpression(),
                    "PersistSiblings")));
            m.Statements.Add(new CodeAttachEventStatement(
                new CodeThisReferenceExpression(),
                "Persisting",
                new CodeDelegateCreateExpression(
                    new CodeTypeReference(typeof(EventHandler<PersistEventArgs>)),
                    new CodeThisReferenceExpression(),
                    "PersistParents")));
            m.Statements.Add(new CodeAttachEventStatement(
                new CodeThisReferenceExpression(),
                "Deleting",
                new CodeDelegateCreateExpression(
                    new CodeTypeReference(typeof(EventHandler<DeleteEventArgs>)),
                    new CodeThisReferenceExpression(),
                    "DeleteSiblings")));
            m.Statements.Add(new CodeAttachEventStatement(
                new CodeThisReferenceExpression(),
                "Deleting",
                new CodeDelegateCreateExpression(
                    new CodeTypeReference(typeof(EventHandler<DeleteEventArgs>)),
                    new CodeThisReferenceExpression(),
                    "DeleteChildren")));
        }
        #endregion
        #region Create PersistChildren
        protected virtual void CreatePersistChildren(Entity entity, CodeTypeDeclaration declaration)
        {
            CodeMemberMethod m = new CodeMemberMethod();
            m.StartDirectives.Add(new CodeRegionDirective(CodeRegionMode.Start, "PersistChildren"));
            m.EndDirectives.Add(new CodeRegionDirective(CodeRegionMode.End, ""));
            m.Attributes = MemberAttributes.Private;
            m.Name = "PersistChildren";
            m.Parameters.Add(new CodeParameterDeclarationExpression(typeof(object), "sender"));
            m.Parameters.Add(new CodeParameterDeclarationExpression(typeof(PersistEventArgs), "e"));
            declaration.Members.Add(m);

            foreach (EntityRelationship relationship in entity.Relationships)
            {
                if (relationship.From == entity && relationship.Type == EntityRelationshipType.Child)
                {
                    IChildRelationship contract = Dependency.GetContract<IChildRelationship>(relationship.With);
                    if (contract != null)
                    {
                        m.Statements.Add(contract.PersistChildrenStatement(relationship));
                    }
                    else
                    {
                        OnWarning(new ESharpBuildWarning(
                            ESharpErrorCode.ES6002,
                            relationship.Elements[0]));
                    }
                }
            }

        }
        #endregion
        #region Create DeleteChildren
        protected virtual void CreateDeleteChildren(Entity entity, CodeTypeDeclaration declaration)
        {
            CodeMemberMethod m = new CodeMemberMethod();
            m.StartDirectives.Add(new CodeRegionDirective(CodeRegionMode.Start, "DeleteChildren"));
            m.EndDirectives.Add(new CodeRegionDirective(CodeRegionMode.End, ""));
            m.Attributes = MemberAttributes.Private;
            m.Name = "DeleteChildren";
            m.Parameters.Add(new CodeParameterDeclarationExpression(typeof(object), "sender"));
            m.Parameters.Add(new CodeParameterDeclarationExpression(typeof(DeleteEventArgs), "e"));
            declaration.Members.Add(m);

            foreach (EntityRelationship relationship in entity.Relationships)
            {
                if (relationship.From == entity && relationship.Type == EntityRelationshipType.Child)
                {
                    IChildRelationship contract = Dependency.GetContract<IChildRelationship>(relationship.With);
                    if (contract != null)
                    {
                        foreach (CodeExpression expression in contract.DeleteChildrenExpressions(relationship))
                        {
                            m.Statements.Add(expression);
                        }
                    }
                    else
                    {
                        OnWarning(new ESharpBuildWarning(
                            ESharpErrorCode.ES0002,
                            relationship.Elements[0]));
                    }
                }
            }

        }
        #endregion
        #region Create Persist_Siblings
        protected virtual void CreatePersistSiblings(Entity entity, CodeTypeDeclaration declaration)
        {
            CodeMemberMethod m = new CodeMemberMethod();
            m.StartDirectives.Add(new CodeRegionDirective(CodeRegionMode.Start, "PersistSiblings"));
            m.EndDirectives.Add(new CodeRegionDirective(CodeRegionMode.End, ""));
            m.Attributes = MemberAttributes.Private;
            m.Name = "PersistSiblings";
            m.Parameters.Add(new CodeParameterDeclarationExpression(typeof(object), "sender"));
            m.Parameters.Add(new CodeParameterDeclarationExpression(typeof(PersistEventArgs), "e"));
            declaration.Members.Add(m);

            foreach (EntityRelationship relationship in entity.Relationships)
            {
                if (relationship.From == entity && relationship.Type == EntityRelationshipType.Sibling)
                {
                    CodeConditionStatement ccs = new CodeConditionStatement(
                        new CodeBinaryOperatorExpression(
                            new CodeFieldReferenceExpression(
                                new CodeThisReferenceExpression(), 
                                "_" + relationship.Name),
                            CodeBinaryOperatorType.IdentityInequality,
                            new CodePrimitiveExpression(null)));

                    ccs.TrueStatements.Add(new CodeMethodInvokeExpression(
                        new CodeFieldReferenceExpression(
                            new CodeThisReferenceExpression(), 
                            "_" + relationship.Name),
                        "Persist",
                        new CodePropertyReferenceExpression(
                            new CodeVariableReferenceExpression("e"),
                            "Transaction"),
                        new CodeFieldReferenceExpression(
                            new CodeTypeReferenceExpression(typeof(PersistType)),
                            "Downward")));

                    m.Statements.Add(ccs);
                }
            }

        }
        #endregion
        #region Create Delete_Siblings
        protected virtual void CreateDeleteSiblings(Entity entity, CodeTypeDeclaration declaration)
        {
            CodeMemberMethod m = new CodeMemberMethod();
            m.StartDirectives.Add(new CodeRegionDirective(CodeRegionMode.Start, "DeleteSiblings"));
            m.EndDirectives.Add(new CodeRegionDirective(CodeRegionMode.End, ""));
            m.Attributes = MemberAttributes.Private;
            m.Name = "DeleteSiblings";
            m.Parameters.Add(new CodeParameterDeclarationExpression(typeof(object), "sender"));
            m.Parameters.Add(new CodeParameterDeclarationExpression(typeof(DeleteEventArgs), "e"));
            declaration.Members.Add(m);

            foreach (EntityRelationship relationship in entity.Relationships)
            {
                if (relationship.From == entity && relationship.Type == EntityRelationshipType.Sibling)
                {
                    m.Statements.Add(new CodeMethodInvokeExpression(
                        new CodePropertyReferenceExpression(
                            new CodeThisReferenceExpression(), 
                            relationship.Name),
                        "Clear"));

                    m.Statements.Add(new CodeMethodInvokeExpression(
                        new CodePropertyReferenceExpression(
                            new CodeThisReferenceExpression(), 
                            relationship.Name),
                        "Persist",
                        new CodePropertyReferenceExpression(
                            new CodeVariableReferenceExpression("e"),
                            "Transaction"),
                        new CodeFieldReferenceExpression(
                            new CodeTypeReferenceExpression(typeof(PersistType)),
                            "Downward")));
                }
            }

        }
        #endregion
        #region Create Persist_Parents
        protected virtual void CreatePersistParents(Entity entity, CodeTypeDeclaration declaration)
        {
            CodeMemberMethod m = new CodeMemberMethod();
            m.StartDirectives.Add(new CodeRegionDirective(CodeRegionMode.Start, "PersistParents"));
            m.EndDirectives.Add(new CodeRegionDirective(CodeRegionMode.End, ""));
            m.Attributes = MemberAttributes.Private;
            m.Name = "PersistParents";
            m.Parameters.Add(new CodeParameterDeclarationExpression(typeof(object), "sender"));
            m.Parameters.Add(new CodeParameterDeclarationExpression(typeof(PersistEventArgs), "e"));
            declaration.Members.Add(m);

            foreach (EntityRelationship relationship in entity.Relationships)
            {
                if (relationship.From == entity && relationship.Type == EntityRelationshipType.Parent)
                {
                    IParentRelationship contract = Dependency.GetContract<IParentRelationship>(relationship.With);
                    if (contract != null)
                    {
                        m.Statements.Add(contract.PersistParentsStatement(relationship));
                    }
                    else
                    {
                        OnWarning(new ESharpBuildWarning(
                            ESharpErrorCode.ES0003,
                            relationship.Elements[0]));
                    }
                }
            }
        }
        #endregion

        #region Create SiblingRelationship
        private void CreateSiblingRelationship(CodeTypeDeclaration declaration, EntityRelationship relationship)
        {
            CodeTypeDeclaration siblinglist = CreateSiblingListDeclaration(relationship);
            CreateSiblingFetch(siblinglist, relationship);
            CreateSiblingLoad(siblinglist, relationship);
            CreateSiblingAdd(siblinglist, relationship);
            CreateSiblingRemoved(siblinglist, relationship);
            CreateSiblingCriteria(siblinglist, relationship);
            declaration.Members.Add(siblinglist);

            CreateSiblingListFieldsAndProperties(declaration, relationship);              
        }

        #region Create Sibling Criteria
        private void CreateSiblingCriteria(CodeTypeDeclaration siblinglist, EntityRelationship relationship)
        {
            CodeTypeDeclaration siblingFetchCriteria = new CodeTypeDeclaration();
            siblingFetchCriteria.BaseTypes.Add(new CodeTypeReference(typeof(NBusiness.Data.CriteriaBase)));
            siblingFetchCriteria.Attributes = MemberAttributes.Public;
            siblingFetchCriteria.Name = relationship.From.Name + relationship.With.Name + "SiblingFetchCriteria";
            siblingFetchCriteria.StartDirectives.Add(new CodeRegionDirective(CodeRegionMode.Start, "Create Sibling Criteria"));
            siblingFetchCriteria.EndDirectives.Add(new CodeRegionDirective(CodeRegionMode.End, ""));

            siblinglist.Members.Add(siblingFetchCriteria);
        } 
        #endregion

        #region Field and Property
        protected virtual void CreateSiblingListFieldsAndProperties(CodeTypeDeclaration declaration, EntityRelationship relationship)
        {
            CodeMemberField f = new CodeMemberField();
            f.Name = "_" + relationship.Name;
            f.Type = new CodeTypeReference(relationship.From.Name + relationship.With.Name + "Collection");
            f.InitExpression = new CodePrimitiveExpression(null);
            f.Attributes = MemberAttributes.Private;
            declaration.Members.Add(f);

            CodeMemberProperty p = new CodeMemberProperty();
            p.Name = relationship.Name;
            p.Type = new CodeTypeReference(relationship.From.Name + relationship.With.Name + "Collection");
            p.Attributes = MemberAttributes.Public;
            p.HasGet = true;
            p.HasSet = false;
            p.EndDirectives.Add(new CodeRegionDirective(CodeRegionMode.End, ""));

            CodeConditionStatement ccs = new CodeConditionStatement(
                new CodeBinaryOperatorExpression(
                    new CodeFieldReferenceExpression(null, "_" + relationship.Name),
                    CodeBinaryOperatorType.IdentityEquality,
                    new CodePrimitiveExpression(null)));

            //The call to create the list
            CodeObjectCreateExpression m = new CodeObjectCreateExpression(
                relationship.From.Name + relationship.With.Name + "Collection",
                new CodeThisReferenceExpression());

            //assign the new list to our private instance
            CodeAssignStatement assign = new CodeAssignStatement(
                new CodeFieldReferenceExpression(null, "_" + relationship.Name),
                m);

            ccs.TrueStatements.Add(assign);

            p.GetStatements.Add(ccs);

            //Return the relationship object
            p.GetStatements.Add(
                new CodeMethodReturnStatement(
                    new CodeFieldReferenceExpression(null, "_" + relationship.Name)));

            declaration.Members.Add(p);
        }
        #endregion

        #region Create SiblingList Removed
        protected virtual void CreateSiblingRemoved(CodeTypeDeclaration declaration, EntityRelationship relationship)
        {
            CodeMemberMethod RemoveSiblingCollection = new CodeMemberMethod();
            RemoveSiblingCollection.StartDirectives.Add(new CodeRegionDirective(CodeRegionMode.Start, "Remove Sibling"));
            RemoveSiblingCollection.EndDirectives.Add(new CodeRegionDirective(CodeRegionMode.End, ""));
            RemoveSiblingCollection.Name = "RemoveSibling";
            RemoveSiblingCollection.Attributes = MemberAttributes.Override | MemberAttributes.Family;
            RemoveSiblingCollection.Parameters.Add(new CodeParameterDeclarationExpression(
                typeof(DbTransaction), "t"));
            RemoveSiblingCollection.Parameters.Add(new CodeParameterDeclarationExpression(
                relationship.With.Name, "sibling"));
            declaration.Members.Add(RemoveSiblingCollection);

            CodeMethodInvokeExpression dbdelete = new CodeMethodInvokeExpression(
                new CodePropertyReferenceExpression(
                    new CodePropertyReferenceExpression(
                        new CodeTypeReferenceExpression(typeof(Settings)),
                        "Instance"),
                    "Database"),
                "Delete",
                new CodeVariableReferenceExpression("t"),
                new CodePrimitiveExpression(relationship.Name + "Delete"));

            RemoveSiblingCollection.Statements.Add(dbdelete);

            foreach (EntityField field in relationship.From.Fields)
            {
                if (field.IsId)
                {
                    dbdelete.Parameters.Add(
                        new CodeObjectCreateExpression(
                            typeof(Parameter),
                            new CodePrimitiveExpression("@" + field.Name),
                            new CodePropertyReferenceExpression(
                                new CodeCastExpression(
                                    new CodeTypeReference(relationship.From.Name),
                                    new CodeVariableReferenceExpression("LeftSibling")),
                                field.Name)));
                }
            }
            foreach (EntityField field in relationship.With.Fields)
            {
                if (field.IsId)
                {
                    dbdelete.Parameters.Add(
                        new CodeObjectCreateExpression(
                            typeof(Parameter),
                            new CodePrimitiveExpression("@" + field.Name),
                            new CodePropertyReferenceExpression(
                                new CodeCastExpression(
                                    new CodeTypeReference(relationship.With.Name),
                                    new CodeVariableReferenceExpression("sibling")),
                                field.Name)));
                }
            }
        }
        #endregion

        #region Create SiblingList Add
        protected virtual void CreateSiblingAdd(CodeTypeDeclaration declaration, EntityRelationship relationship)
        {
            CodeMemberMethod AddSiblingCollection = new CodeMemberMethod();
            AddSiblingCollection.StartDirectives.Add(new CodeRegionDirective(CodeRegionMode.Start, "Add Sibling"));
            AddSiblingCollection.EndDirectives.Add(new CodeRegionDirective(CodeRegionMode.End, ""));
            AddSiblingCollection.Name = "AddSibling";
            AddSiblingCollection.Attributes = MemberAttributes.Override | MemberAttributes.Family;
            AddSiblingCollection.Parameters.Add(new CodeParameterDeclarationExpression(
                typeof(DbTransaction), "t"));
            AddSiblingCollection.Parameters.Add(new CodeParameterDeclarationExpression(
                relationship.With.Name, "sibling"));
            declaration.Members.Add(AddSiblingCollection);

            CodeMethodInvokeExpression dbinsert = new CodeMethodInvokeExpression(
                new CodePropertyReferenceExpression(
                    new CodePropertyReferenceExpression(
                        new CodeTypeReferenceExpression(typeof(Settings)),
                        "Instance"),
                    "Database"),
                "Insert",
                new CodeVariableReferenceExpression("t"),
                new CodePrimitiveExpression(relationship.Name + "Insert"),
                new CodePrimitiveExpression(null));

            AddSiblingCollection.Statements.Add(dbinsert);

            foreach (EntityField field in relationship.From.Fields)
            {
                if (field.IsId)
                {
                    dbinsert.Parameters.Add(
                        new CodeObjectCreateExpression(
                            typeof(Parameter),
                            new CodePrimitiveExpression("@" + field.Name),
                            new CodePropertyReferenceExpression(
                                new CodeCastExpression(
                                    new CodeTypeReference(relationship.From.Name),
                                    new CodeVariableReferenceExpression("LeftSibling")),
                                field.Name)));
                }
            }
            foreach (EntityField field in relationship.With.Fields)
            {
                if (field.IsId)
                {
                    dbinsert.Parameters.Add(
                        new CodeObjectCreateExpression(
                            typeof(Parameter),
                            new CodePrimitiveExpression("@" + field.Name),
                            new CodePropertyReferenceExpression(
                                new CodeCastExpression(
                                    new CodeTypeReference(relationship.With.Name),
                                    new CodeVariableReferenceExpression("sibling")),
                                field.Name)));
                }
            }
        }
        #endregion

        #region Create SiblingList Load
        protected virtual void CreateSiblingLoad(CodeTypeDeclaration declaration, EntityRelationship relationship)
        {
            CodeMemberMethod LoadSiblings = new CodeMemberMethod();
            LoadSiblings.Attributes = MemberAttributes.Override | MemberAttributes.Public;
            LoadSiblings.Name = "Load";
            LoadSiblings.Parameters.Add(new CodeParameterDeclarationExpression(typeof(object), "sender"));
            LoadSiblings.Parameters.Add(new CodeParameterDeclarationExpression(typeof(LoadEventArgs), "e"));
            LoadSiblings.StartDirectives.Add(new CodeRegionDirective(CodeRegionMode.Start, "Load"));
            LoadSiblings.EndDirectives.Add(new CodeRegionDirective(CodeRegionMode.End, ""));
            declaration.Members.Add(LoadSiblings);

            CodeVariableReferenceExpression e = new CodeVariableReferenceExpression("e");
            CodePropertyReferenceExpression reader = new CodePropertyReferenceExpression(e, "Reader");
            CodePropertyReferenceExpression criteria = new CodePropertyReferenceExpression(e, "Criteria");

            CodeIterationStatement i = new CodeIterationStatement(
                new CodeSnippetStatement(""),
                new CodeMethodInvokeExpression(
                    reader,
                    "Read"),
                new CodeSnippetStatement(""));

            i.Statements.Add(
                new CodeMethodInvokeExpression(
                    new CodePropertyReferenceExpression(null, "Items"),
                    "Add",
                    new CodeObjectCreateExpression(
                        new CodeTypeReference(relationship.With.Name),
                        reader,
                        criteria)));

            LoadSiblings.Statements.Add(i);
        } 
        #endregion

        #region Create SiblingList declaration
        protected virtual CodeTypeDeclaration CreateSiblingListDeclaration(EntityRelationship relationship)
        {
            CodeTypeDeclaration siblinglist = new CodeTypeDeclaration();
            siblinglist.StartDirectives.Add(new CodeRegionDirective(CodeRegionMode.Start, "Relationship " + relationship.Name));
            siblinglist.Name = relationship.From.Name + relationship.With.Name + "Collection";
            siblinglist.TypeAttributes = TypeAttributes.NestedPublic;
            siblinglist.IsPartial = true;
            siblinglist.BaseTypes.Add(new CodeTypeReference("SiblingBaseCollection",
                new CodeTypeReference(relationship.With.Name)));
            siblinglist.CustomAttributes.Add(new CodeAttributeDeclaration(
                new CodeTypeReference(typeof(SerializableAttribute))));

            CodeConstructor SiblingListConstructor = new CodeConstructor();
            SiblingListConstructor.Attributes = MemberAttributes.Public;
            SiblingListConstructor.Name = siblinglist.Name;
            SiblingListConstructor.Parameters.Add(new CodeParameterDeclarationExpression(relationship.From.Name, "left"));
            SiblingListConstructor.BaseConstructorArgs.Add(new CodeVariableReferenceExpression("left"));
            siblinglist.Members.Add(SiblingListConstructor);

            CodeConstructor SiblingListConstructor2 = new CodeConstructor();
            SiblingListConstructor2.Attributes = MemberAttributes.Public;
            SiblingListConstructor2.Name = siblinglist.Name;
            SiblingListConstructor2.Parameters.Add(new CodeParameterDeclarationExpression(typeof(DbConnection), "cn"));
            SiblingListConstructor2.Parameters.Add(new CodeParameterDeclarationExpression(relationship.From.Name, "left"));
            SiblingListConstructor2.BaseConstructorArgs.Add(new CodeVariableReferenceExpression("cn"));
            SiblingListConstructor2.BaseConstructorArgs.Add(new CodeVariableReferenceExpression("left"));
            siblinglist.Members.Add(SiblingListConstructor2);

            return siblinglist;
        } 
        #endregion

        #region Create Fetch_SiblingList

        protected virtual void CreateSiblingFetch(CodeTypeDeclaration declaration, EntityRelationship relationship)
        {
            CodeMemberMethod FetchSiblingCollection = new CodeMemberMethod();
            FetchSiblingCollection.StartDirectives.Add(new CodeRegionDirective(CodeRegionMode.Start, "Fetch Sibling"));
            FetchSiblingCollection.Name = "FetchSiblings";
            FetchSiblingCollection.Attributes = MemberAttributes.Override | MemberAttributes.Family;
            FetchSiblingCollection.Parameters.Add(new CodeParameterDeclarationExpression(
                typeof(DbConnection), "cn"));
            FetchSiblingCollection.Parameters.Add(new CodeParameterDeclarationExpression(
                typeof(EntityBase), "left"));
            declaration.Members.Add(FetchSiblingCollection);

            CodeMethodInvokeExpression dbfetch = new CodeMethodInvokeExpression(
                new CodePropertyReferenceExpression(
                    new CodePropertyReferenceExpression(
                        new CodeTypeReferenceExpression(typeof(Settings)),
                        "Instance"),
                    "Database"),
                "FetchCollection",
                new CodeVariableReferenceExpression("cn"),
                new CodePrimitiveExpression(relationship.Name + "FetchAll"),
                new CodeObjectCreateExpression(relationship.From.Name + relationship.With.Name + "SiblingFetchCriteria"),
                new CodeDelegateCreateExpression(
                    new CodeTypeReference(typeof(EventHandler<LoadEventArgs>)),
                    new CodeThisReferenceExpression(),
                    "Load"),
                new CodePrimitiveExpression(-1),
                new CodePrimitiveExpression(-1),
                new CodePrimitiveExpression(null),
                new CodeFieldReferenceExpression(
                    new CodeTypeReferenceExpression(typeof(SortOrder)),
                    "None"));
            FetchSiblingCollection.Statements.Add(dbfetch);

            foreach (EntityField field in relationship.From.Fields)
            {
                if (field.IsId)
                {
                    dbfetch.Parameters.Add(
                        new CodeObjectCreateExpression(
                            typeof(Parameter),
                            new CodePrimitiveExpression("@" + field.Name),
                            new CodePropertyReferenceExpression(
                                new CodeCastExpression(
                                    new CodeTypeReference(relationship.From.Name),
                                    new CodeVariableReferenceExpression("left")),
                                field.Name)));
                }
            }

            FetchSiblingCollection.EndDirectives.Add(new CodeRegionDirective(CodeRegionMode.End, ""));
        }
        #endregion
		#endregion
		#region Create ParentRelationship
		protected virtual void CreateParentRelationship(CodeTypeDeclaration declaration, EntityRelationship relationship)
		{
            IParentRelationship contract = Dependency.GetContract<IParentRelationship>(relationship.With);
            if (contract != null)
            {
                declaration.Members.AddRange(contract.ParentRelationshipMembers(relationship));
            }
            else
            {
                OnWarning(new ESharpBuildWarning(
                    ESharpErrorCode.ES0004,
                    relationship.Elements[0]));
            }
		}
		#endregion
		#region Create ChildRelationship
		protected virtual void CreateChildRelationship(CodeTypeDeclaration d, EntityRelationship relationship)
		{
            IChildRelationship contract = Dependency.GetContract<IChildRelationship>(relationship.With);
            if (contract != null)
            {
                d.Members.AddRange(contract.ChildRelationshipMembers(relationship));
            }
            else
            {
                OnWarning(new ESharpBuildWarning(
                    ESharpErrorCode.ES0005,
                    relationship.Elements[0]));
            }
		}
		#endregion
		#endregion

		#region Create Data Access Methods
		protected virtual void CreateDataAccessMethods(Entity entity, CodeTypeDeclaration declaration)
		{
            CreateEntityFetch(entity, declaration);
            CreateLoad(entity, declaration);
            CreateEntityInsert(entity, declaration);
			CreateEntityUpdate(entity, declaration);
			CreateEntityDelete(entity, declaration);
		}

		#region Create Entity_Delete
        protected virtual void CreateEntityDelete(Entity entity, CodeTypeDeclaration declaration)
        {
            CodeMemberMethod m = new CodeMemberMethod();
            m.StartDirectives.Add(new CodeRegionDirective(CodeRegionMode.Start, "Delete"));
            m.Attributes = MemberAttributes.Family | MemberAttributes.Override;
            m.Name = "DeleteEntity";
            m.Parameters.Add(new CodeParameterDeclarationExpression(typeof(DbTransaction), "t"));

            CodeMethodInvokeExpression mi = new CodeMethodInvokeExpression(
                new CodePropertyReferenceExpression(
                    new CodePropertyReferenceExpression(
                        new CodeTypeReferenceExpression(typeof(Settings)),
                        "Instance"),
                    "Database"),
                "Delete",
                new CodeVariableReferenceExpression("t"),
                new CodePrimitiveExpression(entity.Name + "Delete"));

            foreach (EntityField f in entity.Fields)
            {
                if (f.IsId)
                {
                    mi.Parameters.Add(
                        new CodeObjectCreateExpression(
                            typeof(Parameter),
                            new CodePrimitiveExpression("@" + f.Name),
                            new CodeFieldReferenceExpression(null, "_" + f.Name)));
                }
            }

            m.Statements.Add(mi);
            m.EndDirectives.Add(new CodeRegionDirective(CodeRegionMode.End, ""));

            declaration.Members.Add(m);
        } 
		#endregion

		#region Create Entity_Update
        protected virtual void CreateEntityUpdate(Entity entity, CodeTypeDeclaration declaration)
        {
            CodeMemberMethod m = new CodeMemberMethod();
            m.StartDirectives.Add(new CodeRegionDirective(CodeRegionMode.Start, "Update"));
            m.Attributes = MemberAttributes.Family | MemberAttributes.Override;
            m.Name = "UpdateEntity";
            m.Parameters.Add(new CodeParameterDeclarationExpression(typeof(DbTransaction), "t"));

            CodeMethodInvokeExpression mi = new CodeMethodInvokeExpression(
                new CodePropertyReferenceExpression(
                    new CodePropertyReferenceExpression(
                        new CodeTypeReferenceExpression(
                            typeof(Settings)),
                        "Instance"),
                    "Database"),
                "Update",
                new CodeVariableReferenceExpression("t"),
                new CodePrimitiveExpression(entity.Name + "Update"));

            foreach (EntityField f in entity.Fields)
            {
                mi.Parameters.Add(
                    new CodeObjectCreateExpression(
                        typeof(Parameter),
                        new CodePrimitiveExpression("@" + f.Name),
                        new CodeFieldReferenceExpression(null, "_" + f.Name)));
            }

            m.Statements.Add(mi);
            m.EndDirectives.Add(new CodeRegionDirective(CodeRegionMode.End, ""));

            declaration.Members.Add(m);
        } 
		#endregion

        #region Create Insert_Entity
        protected virtual void CreateEntityInsert(Entity entity, CodeTypeDeclaration declaration)
        {
            CodeMemberMethod m = new CodeMemberMethod();
            m.StartDirectives.Add(new CodeRegionDirective(CodeRegionMode.Start, "Insert"));
            m.Attributes = MemberAttributes.Family | MemberAttributes.Override;
            m.Name = "InsertEntity";
            m.Parameters.Add(new CodeParameterDeclarationExpression(typeof(DbTransaction), "t"));

            CodeMethodInvokeExpression mi = new CodeMethodInvokeExpression(
                new CodePropertyReferenceExpression(
                    new CodePropertyReferenceExpression(
                        new CodeTypeReferenceExpression(typeof(Settings)),
                        "Instance"),
                    "Database"),
                "Insert",
                new CodeVariableReferenceExpression("t"),
                new CodePrimitiveExpression(entity.Name + "Insert"),
                new CodePrimitiveExpression(null));

            bool hasidentity = false;
            foreach (EntityField f in entity.Fields)
            {
                if (f.IsAuto)
                {
                    CodeObjectCreateExpression parameter = new CodeObjectCreateExpression(
                        typeof(Parameter),
                        new CodePrimitiveExpression("@" + f.Name),
                        new CodeFieldReferenceExpression(null, "_" + f.Name),
                        new CodeFieldReferenceExpression(
                            new CodeTypeReferenceExpression(typeof(ParameterDirection)),
                            "Output"));

                    mi.Parameters.Add(parameter);
                    hasidentity = true;
                }
                else
                {
                    CodeObjectCreateExpression parameter = new CodeObjectCreateExpression(
                        typeof(Parameter),
                        new CodePrimitiveExpression("@" + f.Name),
                        new CodeFieldReferenceExpression(null, "_" + f.Name));
                    mi.Parameters.Add(parameter);
                }
            }
            if (hasidentity)
            {
                CodeDelegateCreateExpression loadIdentity = new CodeDelegateCreateExpression(
                    new CodeTypeReference(typeof(LoadIdentityHandler)),
                    new CodeThisReferenceExpression(),
                    "LoadIdentity");
                mi.Parameters[2] = loadIdentity;

                CodeMemberMethod li = new CodeMemberMethod();
                li.StartDirectives.Add(new CodeRegionDirective(CodeRegionMode.Start, "LoadIdentity"));
                li.Attributes = MemberAttributes.Private;
                li.Name = "LoadIdentity";
                li.Parameters.Add(new CodeParameterDeclarationExpression(typeof(Dictionary<string, object>), "Parameters"));

                foreach (EntityField f in entity.Fields)
                {
                    if (f.IsAuto)
                    {
                        CodeFieldReferenceExpression field = new CodeFieldReferenceExpression(
                            null,
                            "_" + f.Name);
                        CodeMethodInvokeExpression changeType = new CodeMethodInvokeExpression(
                            new CodeTypeReferenceExpression(typeof(Convert)),
                            "ChangeType",
                            new CodeArrayIndexerExpression(
                                new CodeVariableReferenceExpression("Parameters"),
                                new CodePrimitiveExpression("@" + f.Name)),
                            new CodeTypeOfExpression(f.Type));

                        CodeCastExpression castToType = new CodeCastExpression(f.Type, changeType);

                        CodeAssignStatement setField = new CodeAssignStatement(
                            field,
                            castToType);

                        li.Statements.Add(setField);
                        li.Statements.Add(new CodeMethodInvokeExpression(
                                    null,
                                    "OnPropertyChanged",
                                    new CodePrimitiveExpression(f.Name)));
                    }
                }

                li.EndDirectives.Add(new CodeRegionDirective(CodeRegionMode.End, ""));

                declaration.Members.Add(li);
            }

            m.Statements.Add(mi);
            m.EndDirectives.Add(new CodeRegionDirective(CodeRegionMode.End, ""));

            declaration.Members.Add(m);
        } 
        #endregion

        #region Create Load
        protected virtual void CreateLoad(Entity entity, CodeTypeDeclaration declaration)
        {
            CodeMemberMethod m = new CodeMemberMethod();
            m.StartDirectives.Add(new CodeRegionDirective(CodeRegionMode.Start, "LoadEntity"));
            m.Attributes = MemberAttributes.Family | MemberAttributes.Override;
            m.Name = "LoadEntity";
            m.Parameters.Add(new CodeParameterDeclarationExpression(typeof(object), "sender"));
            m.Parameters.Add(new CodeParameterDeclarationExpression(typeof(LoadEventArgs), "e"));

            CodeVariableReferenceExpression e = new CodeVariableReferenceExpression("e");
            CodePropertyReferenceExpression reader = new CodePropertyReferenceExpression(e, "Reader");

            foreach (EntityField f in entity.Fields)
            {
                CodeMethodInvokeExpression getvalue = new CodeMethodInvokeExpression(
                    reader,
                    "GetValue",
                    new CodePrimitiveExpression(f.Name));
                getvalue.Method.TypeArguments.Add(new CodeTypeReference(f.Type));

                m.Statements.Add(new CodeAssignStatement(
                    new CodeFieldReferenceExpression(null, "_" + f.Name),
                    getvalue));
            }

            m.EndDirectives.Add(new CodeRegionDirective(CodeRegionMode.End, ""));

            declaration.Members.Add(m);
        }  
        #endregion

        #region Create Entity_Fetch
        protected virtual void CreateEntityFetch(Entity entity, CodeTypeDeclaration declaration)
        {
            CodeMemberMethod m = new CodeMemberMethod();
            m.StartDirectives.Add(new CodeRegionDirective(CodeRegionMode.Start, "Fetch"));
            m.Attributes = MemberAttributes.Family | MemberAttributes.Override;
            m.Name = "FetchEntity";
            m.Parameters.Add(new CodeParameterDeclarationExpression(typeof(DbConnection), "cn"));
            m.Parameters.Add(new CodeParameterDeclarationExpression(typeof(NBusiness.Data.CriteriaBase), "c"));

            CodeVariableReferenceExpression criteria = new CodeVariableReferenceExpression("c");

            CodeMethodInvokeExpression mi = new CodeMethodInvokeExpression(
                new CodePropertyReferenceExpression(
                    new CodePropertyReferenceExpression(
                        new CodeTypeReferenceExpression(typeof(Settings)),
                        "Instance"),
                    "Database"),
                "Fetch",
                new CodeVariableReferenceExpression("cn"),
                new CodePrimitiveExpression(entity.Name + "Fetch"),
                criteria,
                new CodeDelegateCreateExpression(
                    new CodeTypeReference(typeof(EventHandler<LoadEventArgs>)),
                    new CodeThisReferenceExpression(),
                    "Load"));

            CodeMethodReferenceExpression getType = new CodeMethodReferenceExpression(
                new CodeThisReferenceExpression(),
                "GetType");

            CodeMethodInvokeExpression isAssignableFrom = new CodeMethodInvokeExpression(
                        new CodeTypeOfExpression(entity.Name + "Criteria"),
                        "IsAssignableFrom",
                        new CodeMethodInvokeExpression(
                            criteria,
                            "GetType"));

            CodeConditionStatement ifGeneratedCriteria = new CodeConditionStatement(
                isAssignableFrom);

            foreach (EntityField f in entity.Fields)
            {
                if (f.IsId)
                {

                    CodePropertyReferenceExpression fieldProperty = new CodePropertyReferenceExpression(
                        new CodeCastExpression(
                        new CodeTypeReference(entity.Name + "Criteria"),
                        criteria),
                        f.Name);

                    CodeObjectCreateExpression paramInit = new CodeObjectCreateExpression(
                        typeof(Parameter),
                        new CodePrimitiveExpression("@" + f.Name),
                        fieldProperty);

                    CodeVariableDeclarationStatement paramDeclaration = new CodeVariableDeclarationStatement(
                        typeof(Parameter),
                        f.Name + "Parameter",
                        paramInit);

                    ifGeneratedCriteria.TrueStatements.Add(paramDeclaration);
                    mi.Parameters.Add(new CodeVariableReferenceExpression(paramDeclaration.Name));
                }
            }
            ifGeneratedCriteria.TrueStatements.Add(mi);

            m.Statements.Add(ifGeneratedCriteria);
            m.EndDirectives.Add(new CodeRegionDirective(CodeRegionMode.End, ""));

            declaration.Members.Add(m);
        }   
        #endregion
		#endregion

        #region Create GetInfoObject

        private void CreateInfoObjectMethods(Entity entity, CodeTypeDeclaration d)
        {
            CreateGetInfoObject(entity, d);
            CreateLoadByInfoObject(entity, d);
        }

        private void CreateLoadByInfoObject(Entity entity, CodeTypeDeclaration d)
        {
            IInfoObject getInfoObject = Dependency.GetContract<IInfoObject>(entity);
            if (getInfoObject != null)
            {
                CodeMemberMethod m = getInfoObject.LoadByInfoObjectMethod(entity);
                d.Members.Add(getInfoObject.GetLoadByInfoObjectCriteriaDeclaration(entity));
                d.Members.Add(m);
            }
            else
            {
                EntityElement el = entity.Elements.Find(new Predicate<EntityElement>(delegate(EntityElement e)
                {
                    return e.Type == EntityElementType.EntityDeclaration;
                }));

                OnWarning(new ESharpBuildWarning(
                    ESharpErrorCode.ES0006,
                    el));
            }
        }

        protected virtual void CreateGetInfoObject(Entity entity, CodeTypeDeclaration declaration)
        {
            IInfoObject getInfoObject = Dependency.GetContract<IInfoObject>(entity);
            if(getInfoObject != null)
            {
                CodeMemberMethod m = getInfoObject.GetInfoObjectMethod(entity);
                declaration.Members.Add(m);
            }
            else
            {
                EntityElement el = entity.Elements.Find(new Predicate<EntityElement>(delegate(EntityElement e)
                {
                    return e.Type == EntityElementType.EntityDeclaration;
                }));

                OnWarning(new ESharpBuildWarning(
                    ESharpErrorCode.ES0006,
                    el));
            }
        }
        #endregion

        #region Warning
        public event EventHandler<EntityBuildErrorEventArgs> Warning;
        protected virtual void OnWarning(ESharpBuildWarning warning)
        {
            if (Warning != null) Warning(this, new EntityBuildErrorEventArgs(warning));
        }
        #endregion

        #region IParentRelationship Members

        CodeTypeMember[] IParentRelationship.ParentRelationshipMembers(EntityRelationship relationship)
        {
            List<CodeTypeMember> members = new List<CodeTypeMember>();

            //Create the field for the relationship, lazy load if null by default
            CodeMemberField f = new CodeMemberField();
            f.Name = string.Format("_{0}", relationship.Name);
            f.Type = new CodeTypeReference(relationship.With.Name);
            f.InitExpression = new CodePrimitiveExpression(null);
            f.Attributes = MemberAttributes.Private;
            f.StartDirectives.Add(new CodeRegionDirective(CodeRegionMode.Start, "Relationship " + relationship.Name));

            //create the property
            CodeMemberProperty p = new CodeMemberProperty();
            p.Name = relationship.Name;
            p.Type = new CodeTypeReference(relationship.With.Name);
            p.Attributes = MemberAttributes.Public;

            //Attach to the Changed event of the object after loaded.
            CodeAttachEventStatement changed = new CodeAttachEventStatement(
               new CodeFieldReferenceExpression(
                    new CodeThisReferenceExpression(), 
                    "_" + relationship.Name),
               "PropertyChanged",
               new CodeDelegateCreateExpression(
                new CodeTypeReference(typeof(PropertyChangedEventHandler)),
                new CodeThisReferenceExpression(),
                relationship.Name + "PropertyChanged"));

            //Fetch the object
            CodeObjectCreateExpression fetchCreate = new CodeObjectCreateExpression(relationship.With.Name);
            foreach (EntityField field in relationship.On.Keys)
            {
                fetchCreate.Parameters.Add(
                    new CodeCastExpression(relationship.On[field].Type,
                    new CodeVariableReferenceExpression(field.Name)));
            }

            CodeAssignStatement fetch = new CodeAssignStatement(
                new CodeFieldReferenceExpression(null, "_" + relationship.Name),
                fetchCreate);

            //If its null try to fetch, catch exceptions then create.
            CodeConditionStatement ccs = new CodeConditionStatement();

            CodeBinaryOperatorExpression condition = new CodeBinaryOperatorExpression(
                    new CodeVariableReferenceExpression("_" + relationship.Name),
                     CodeBinaryOperatorType.IdentityEquality,
                     new CodePrimitiveExpression(null));

            foreach (EntityField field in relationship.On.Keys)
            {
                CodeBinaryOperatorExpression andCondition = new CodeBinaryOperatorExpression(
                    condition,
                    CodeBinaryOperatorType.BooleanAnd,
                    new CodeBinaryOperatorExpression(
                        new CodeVariableReferenceExpression("_" + field.Name),
                        CodeBinaryOperatorType.IdentityInequality,
                        new CodeDefaultValueExpression(new CodeTypeReference(field.Type))));
                condition = andCondition;
            }
            // TODO: Add logic to only update the field if the propertyname is the correct propertyname...

            //add to the if and add if to the set
            ccs.Condition = condition;
            ccs.TrueStatements.Add(fetch);
            ccs.TrueStatements.Add(changed);
            p.GetStatements.Add(ccs);

            //Return the object
            p.GetStatements.Add(
                new CodeMethodReturnStatement(
                    new CodeFieldReferenceExpression(null, "_" + relationship.Name)));

            //Set the value
            CodeConditionStatement ifNull = new CodeConditionStatement(
                new CodeBinaryOperatorExpression(
                    new CodeFieldReferenceExpression(null, "_" + relationship.Name),
                    CodeBinaryOperatorType.IdentityInequality,
                    new CodePrimitiveExpression(null)));

            ifNull.TrueStatements.Add(
                new CodeRemoveEventStatement(
                    new CodeFieldReferenceExpression(
                        new CodeThisReferenceExpression(), 
                        "_" + relationship.Name),
                    "PropertyChanged",
                    new CodeDelegateCreateExpression(
                        new CodeTypeReference(typeof(PropertyChangedEventHandler)),
                        new CodeThisReferenceExpression(),
                        relationship.Name + "PropertyChanged")));

            p.SetStatements.Add(ifNull);

            p.SetStatements.Add(
                new CodeAssignStatement(
                    new CodeFieldReferenceExpression(null, "_" + relationship.Name),
                    new CodePropertySetValueReferenceExpression()));

            ifNull = new CodeConditionStatement(
                new CodeBinaryOperatorExpression(
                    new CodePropertySetValueReferenceExpression(),
                    CodeBinaryOperatorType.IdentityInequality,
                    new CodePrimitiveExpression(null)));

            foreach (EntityField field in relationship.On.Keys)
            {
                ifNull.FalseStatements.Add(new CodeAssignStatement(
                    new CodeVariableReferenceExpression("_" + field.Name),
                    new CodeDefaultValueExpression(new CodeTypeReference(field.Type))));
            }

            //Create the method for handling changes to this object.
            CodeMemberMethod handler = new CodeMemberMethod();
            handler.Attributes = MemberAttributes.Private;
            handler.Name = relationship.Name + "PropertyChanged";
            handler.Parameters.Add(new CodeParameterDeclarationExpression(typeof(object), "sender"));
            handler.Parameters.Add(new CodeParameterDeclarationExpression(typeof(PropertyChangedEventArgs), "e"));
            handler.EndDirectives.Add(new CodeRegionDirective(CodeRegionMode.End, ""));

            foreach (EntityField fld in relationship.On.Keys)
            {
                //assign values of this to the foreign object if the object changes.
                ifNull.TrueStatements.Add(
                    new CodeAssignStatement(
                        new CodeFieldReferenceExpression(
                            new CodeThisReferenceExpression(), 
                            "_" + fld.Name),
                        new CodePropertyReferenceExpression(
                            new CodePropertySetValueReferenceExpression(),
                            relationship.On[fld].Name)));

                handler.Statements.Add(
                    new CodeAssignStatement(
                        new CodeFieldReferenceExpression(
                            new CodeThisReferenceExpression(), 
                            "_" + fld.Name),
                        new CodePropertyReferenceExpression(
                            new CodePropertyReferenceExpression(
                                new CodeThisReferenceExpression(),
                                relationship.Name),
                            relationship.On[fld].Name)));
            }

            //Attach/Detach events for assigning to the relationshp property directly
            ifNull.TrueStatements.Add(changed);

            //Create code to add this item to the relationships collection.
            members.Add(f);

            EntityRelationship backRelationship = relationship.With.Relationships.Find(
                new Predicate<EntityRelationship>(delegate(EntityRelationship r)
                {
                    return r.With == relationship.From;
                }));

            if (backRelationship != null)
            {
                IChildRelationship contract = Dependency.GetContract<IChildRelationship>(backRelationship.With);
                if (contract != null)
                {
                    CodeTypeMember[] ctm;
                    CodeStatement statement = contract.BackRelationshipAdd(relationship, backRelationship, out ctm);

                    ifNull.TrueStatements.Add(statement);
                    members.AddRange(ctm);
                }
                else
                {
                    OnWarning(new ESharpBuildWarning(
                        ESharpErrorCode.ES0005,
                        relationship.Elements[0]));
                }

            }
            p.SetStatements.Add(ifNull);

            members.Add(p);
            members.Add(handler);
            return members.ToArray();
        }

        public CodeStatement PersistParentsStatement(EntityRelationship relationship)
        {
            CodeConditionStatement ccs = new CodeConditionStatement(
                new CodeBinaryOperatorExpression(
                    new CodeFieldReferenceExpression(
                        new CodeThisReferenceExpression(), 
                        "_" + relationship.Name),
                    CodeBinaryOperatorType.IdentityInequality,
                    new CodePrimitiveExpression(null)));

            ccs.TrueStatements.Add(new CodeMethodInvokeExpression(
                new CodeFieldReferenceExpression(
                    new CodeThisReferenceExpression(), 
                    "_" + relationship.Name),
                "Persist",
                new CodePropertyReferenceExpression(
                    new CodeVariableReferenceExpression("e"),
                    "Transaction"),
                new CodeFieldReferenceExpression(
                    new CodeTypeReferenceExpression(typeof(PersistType)),
                    "Upward")));

            return ccs;
        }

        public CodeMemberMethod[] FetchByParentMethods(EntityRelationship relationship)
        {
            List<CodeMemberMethod> members = new List<CodeMemberMethod>();

            CodeMemberMethod m = new CodeMemberMethod();
            m.Attributes = MemberAttributes.Static | MemberAttributes.Public;
            m.Name = "FetchBy" + relationship.From.Name;
            m.ReturnType = new CodeTypeReference(relationship.With.Name + "Collection");
            m.StartDirectives.Add(new CodeRegionDirective(CodeRegionMode.Start, m.Name));
            members.Add(m);

            CodeMemberMethod m2 = new CodeMemberMethod();
            m2.Attributes = MemberAttributes.Static | MemberAttributes.Public;
            m2.Name = "FetchBy" + relationship.From.Name;
            m2.ReturnType = new CodeTypeReference(relationship.With.Name + "Collection");
            m2.EndDirectives.Add(new CodeRegionDirective(CodeRegionMode.End, ""));
            members.Add(m2);

            CodeVariableDeclarationStatement Collection = new CodeVariableDeclarationStatement(
                relationship.With.Name + "Collection",
                "items");

            CodeObjectCreateExpression newCollection = new CodeObjectCreateExpression(
                relationship.With.Name + "Collection");

            CodeVariableDeclarationStatement criteria = new CodeVariableDeclarationStatement(
                "FetchBy" + relationship.From.Name + "Criteria",
                "criteria");

            CodeObjectCreateExpression fetchByCriteria = new CodeObjectCreateExpression(
                "FetchBy" + relationship.From.Name + "Criteria");
            criteria.InitExpression = fetchByCriteria;

            Collection.InitExpression = newCollection;
            newCollection.Parameters.Add(new CodeVariableReferenceExpression("criteria"));

            CodeVariableDeclarationStatement i = new CodeVariableDeclarationStatement(typeof(int), "i");
            CodeMethodReturnStatement mReturn = new CodeMethodReturnStatement();
            CodeMethodInvokeExpression m2Factory = new CodeMethodInvokeExpression(
                null,
                "FetchBy" + relationship.From.Name);

            mReturn.Expression = m2Factory;
            m.Statements.Add(i);
            m.Statements.Add(mReturn);


            m2.Statements.Add(criteria);
            m2.Statements.Add(Collection);

            foreach (EntityField f in relationship.From.Fields)
            {
                if (f.IsId)
                {
                    m.Parameters.Add(new CodeParameterDeclarationExpression(f.Type, f.Name));
                    m2.Parameters.Add(new CodeParameterDeclarationExpression(f.Type, f.Name));
                    m2Factory.Parameters.Add(new CodeVariableReferenceExpression(f.Name));
                    fetchByCriteria.Parameters.Add(new CodeVariableReferenceExpression(f.Name));
                }
            }
            m2Factory.Parameters.Add(new CodePrimitiveExpression(0));
            m2Factory.Parameters.Add(new CodePrimitiveExpression(-1));
            m2Factory.Parameters.Add(new CodePrimitiveExpression(null));
            m2Factory.Parameters.Add(new CodeFieldReferenceExpression(
                new CodeTypeReferenceExpression(typeof(SortOrder)),
                "None"));
            m2Factory.Parameters.Add(
                new CodeDirectionExpression(
                    FieldDirection.Out,    
                    new CodeVariableReferenceExpression("i")));

            m2.Parameters.Add(new CodeParameterDeclarationExpression(typeof(int), "startIndex"));
            m2.Parameters.Add(new CodeParameterDeclarationExpression(typeof(int), "maxRows"));
            m2.Parameters.Add(new CodeParameterDeclarationExpression(typeof(string), "fieldOrder"));
            m2.Parameters.Add(new CodeParameterDeclarationExpression(typeof(SortOrder), "sortOrder"));

            fetchByCriteria.Parameters.Add(new CodeVariableReferenceExpression("startIndex"));
            fetchByCriteria.Parameters.Add(new CodeVariableReferenceExpression("maxRows"));
            fetchByCriteria.Parameters.Add(new CodeVariableReferenceExpression("fieldOrder"));
            fetchByCriteria.Parameters.Add(new CodeVariableReferenceExpression("sortOrder"));

            CodeParameterDeclarationExpression totalRows = new CodeParameterDeclarationExpression(typeof(int), "totalRows");
            totalRows.Direction = FieldDirection.Out;
            m2.Parameters.Add(totalRows);

            CodeAssignStatement assignTotalRows = new CodeAssignStatement(
                new CodeVariableReferenceExpression("totalRows"),
                new CodePropertyReferenceExpression(
                    new CodeVariableReferenceExpression("criteria"),
                    "TotalEntities"));
            m2.Statements.Add(assignTotalRows);

            CodeMethodReturnStatement ret = new CodeMethodReturnStatement();
            ret.Expression = new CodeVariableReferenceExpression("items");

            m2.Statements.Add(ret);

            return members.ToArray();
        }

        public CodeTypeDeclaration FetchByParentCriteria(EntityRelationship relationship)
        {
            CodeTypeDeclaration t = new CodeTypeDeclaration();
            t.TypeAttributes = System.Reflection.TypeAttributes.NestedAssembly;
            t.Name = "FetchBy" + relationship.From.Name + "Criteria";
            t.StartDirectives.Add(new CodeRegionDirective(CodeRegionMode.Start, t.Name));
            t.EndDirectives.Add(new CodeRegionDirective(CodeRegionMode.End, ""));
            t.BaseTypes.Add(new CodeTypeReference(typeof(NBusiness.Data.CriteriaBase)));

            CodeConstructor c = new CodeConstructor();
            c.Name = t.Name;
            c.Attributes = MemberAttributes.Public;
            t.Members.Add(c);

            CodeConstructor c2 = new CodeConstructor();
            c2.Attributes = MemberAttributes.Public;
            t.Members.Add(c2);

            foreach (EntityField f in relationship.On.Values)
            {
                CodeParameterDeclarationExpression fieldParam = new CodeParameterDeclarationExpression(
                    f.Type,
                    f.Name);
                c.Parameters.Add(fieldParam);
                c2.Parameters.Add(fieldParam);
                c.ChainedConstructorArgs.Add(new CodeVariableReferenceExpression(f.Name));

                CodeMemberField field = new CodeMemberField();
                field.Attributes = MemberAttributes.Private;
                field.Name = "_" + f.Name;
                field.Type = new CodeTypeReference(f.Type);
                t.Members.Add(field);

                CodeMemberProperty p = new CodeMemberProperty();
                p.Attributes = MemberAttributes.Public;
                p.Type = new CodeTypeReference(f.Type);
                p.Name = f.Name;
                p.GetStatements.Add(new CodeMethodReturnStatement(
                    new CodeFieldReferenceExpression(null, "_" + f.Name)));

                t.Members.Add(p);

                CodeAssignStatement assign = new CodeAssignStatement(
                    new CodeFieldReferenceExpression(null, "_" + f.Name),
                    new CodeVariableReferenceExpression(f.Name));

                c2.Statements.Add(assign);

            }
            c.ChainedConstructorArgs.Add(new CodePrimitiveExpression(0));
            c.ChainedConstructorArgs.Add(new CodePrimitiveExpression(-1));
            c.ChainedConstructorArgs.Add(new CodePrimitiveExpression(null));
            c.ChainedConstructorArgs.Add(new CodeFieldReferenceExpression(
                new CodeTypeReferenceExpression(typeof(SortOrder)), "None"));

            c2.Parameters.Add(new CodeParameterDeclarationExpression(typeof(int), "startIndex"));
            c2.Parameters.Add(new CodeParameterDeclarationExpression(typeof(int), "maxRows"));
            c2.Parameters.Add(new CodeParameterDeclarationExpression(typeof(string), "fieldOrder"));
            c2.Parameters.Add(new CodeParameterDeclarationExpression(typeof(SortOrder), "sortOrder"));

            c2.BaseConstructorArgs.Add(new CodeVariableReferenceExpression("startIndex"));
            c2.BaseConstructorArgs.Add(new CodeVariableReferenceExpression("maxRows"));
            c2.BaseConstructorArgs.Add(new CodeVariableReferenceExpression("fieldOrder"));
            c2.BaseConstructorArgs.Add(new CodeVariableReferenceExpression("sortOrder"));

            return t;
        }

        public CodeConditionStatement FetchByParentCollection(EntityRelationship relationship, CodeVariableReferenceExpression criteriaReference)
        {
            CodeVariableDeclarationStatement fetchByCriteriaDeclaration = new CodeVariableDeclarationStatement(
                        new CodeTypeReference("FetchBy" + relationship.From.Name + "Criteria"),
                        "fc",
                        new CodeCastExpression("FetchBy" + relationship.From.Name + "Criteria", criteriaReference));

            CodeVariableReferenceExpression fetchByCriteriaReference = new CodeVariableReferenceExpression("fc");

            CodeMethodInvokeExpression isAssignableFrom = new CodeMethodInvokeExpression(
                new CodeTypeOfExpression("FetchBy" + relationship.From.Name + "Criteria"),
                "IsAssignableFrom",
                new CodeMethodInvokeExpression(
                    criteriaReference,
                    "GetType"));

            CodeConditionStatement c2 = new CodeConditionStatement(
                isAssignableFrom,
                fetchByCriteriaDeclaration);

            CodeMethodInvokeExpression fetchBy = new CodeMethodInvokeExpression(
                new CodePropertyReferenceExpression(
                    new CodePropertyReferenceExpression(
                        new CodeTypeReferenceExpression(typeof(Settings)), "Instance"),
                        "Database"),
                "FetchCollection",
                new CodeVariableReferenceExpression("cn"),
                new CodePrimitiveExpression(relationship.With.Name + "CollectionFetchBy" + relationship.From.Name),
                criteriaReference,
                new CodeDelegateCreateExpression(
                    new CodeTypeReference(typeof(EventHandler<LoadEventArgs>)),
                    new CodeThisReferenceExpression(),
                    "Load"),
                new CodePropertyReferenceExpression(fetchByCriteriaReference, "StartIndex"),
                new CodePropertyReferenceExpression(fetchByCriteriaReference, "MaxEntities"),
                new CodePropertyReferenceExpression(fetchByCriteriaReference, "SortField"),
                new CodePropertyReferenceExpression(fetchByCriteriaReference, "SortOrder"));

            CodeAssignStatement totalEntities = new CodeAssignStatement(
                new CodePropertyReferenceExpression(fetchByCriteriaReference, "TotalEntities"),
                fetchBy);
            c2.TrueStatements.Add(totalEntities);

            foreach (EntityField f in relationship.On.Keys)
            {
                CodeObjectCreateExpression newParameter = new CodeObjectCreateExpression(
                    new CodeTypeReference(typeof(Parameter)),
                    new CodePrimitiveExpression("@" + f.Name),
                    new CodePropertyReferenceExpression(
                        fetchByCriteriaReference,
                        f.Name));
                fetchBy.Parameters.Add(newParameter);
            }

            return c2;
        }
        #endregion

        #region Create Actions
        protected virtual void CreateActions(Entity entity, CodeTypeDeclaration declaration)
        {
            CodeMemberMethod initializeActions = new CodeMemberMethod();
            initializeActions.Attributes = MemberAttributes.Family | MemberAttributes.Override;
            initializeActions.Name = "InitializeActions";
            initializeActions.StartDirectives.Add(new CodeRegionDirective(CodeRegionMode.Start, "InitializeActions"));
            
            declaration.Members.Add(initializeActions);

            foreach (EntityAction a in entity.Actions)
            {
                CodeTypeReference listenerType = null;
                CodeTypeReference argumentType = null;
                switch (a.When)
                {
                    #region Loading, Loaded
                    case EntityActionType.Loaded:
                        {
                            listenerType = new CodeTypeReference(typeof(EventHandler<LoadEventArgs>));
                            argumentType = new CodeTypeReference(typeof(LoadEventArgs));
                            break;
                        }
                    case EntityActionType.Loading:
                        {
                            listenerType = new CodeTypeReference(typeof(EventHandler<LoadEventArgs>));
                            argumentType = new CodeTypeReference(typeof(LoadEventArgs));
                            break;
                        } 
                    #endregion
                    #region Fetching, Fetched
                    case EntityActionType.Fetched:
                        {
                            listenerType = new CodeTypeReference(typeof(EventHandler<FetchEventArgs>));
                            argumentType = new CodeTypeReference(typeof(FetchEventArgs));
                            break;
                        }
                    case EntityActionType.Fetching:
                        {
                            listenerType = new CodeTypeReference(typeof(EventHandler<FetchEventArgs>));
                            argumentType = new CodeTypeReference(typeof(FetchEventArgs));
                            break;
                        } 
                    #endregion
                    #region Persisting, Persisted
                    case EntityActionType.Persisting:
                        {
                            listenerType = new CodeTypeReference(typeof(EventHandler<PersistEventArgs>));
                            argumentType = new CodeTypeReference(typeof(PersistEventArgs));
                            break;
                        }
                    case EntityActionType.Persisted:
                        {
                            listenerType = new CodeTypeReference(typeof(EventHandler<PersistEventArgs>));
                            argumentType = new CodeTypeReference(typeof(PersistEventArgs));
                            break;
                        } 
                    #endregion
                    #region Inserting, Inserted
                    case EntityActionType.Inserted:
                        {
                            listenerType = new CodeTypeReference(typeof(EventHandler<InsertEventArgs>));
                            argumentType = new CodeTypeReference(typeof(InsertEventArgs));
                            break;
                        }
                    case EntityActionType.Inserting:
                        {
                            listenerType = new CodeTypeReference(typeof(EventHandler<InsertEventArgs>));
                            argumentType = new CodeTypeReference(typeof(InsertEventArgs));
                            break;
                        }
                    #endregion
                    #region Updating, Updated
                    case EntityActionType.Updated:
                        {
                            listenerType = new CodeTypeReference(typeof(EventHandler<UpdateEventArgs>));
                            argumentType = new CodeTypeReference(typeof(UpdateEventArgs));
                            break;
                        }
                    case EntityActionType.Updating:
                        {
                            listenerType = new CodeTypeReference(typeof(EventHandler<UpdateEventArgs>));
                            argumentType = new CodeTypeReference(typeof(UpdateEventArgs));
                            break;
                        } 
                    #endregion
                    #region Deleting, Deleted
                    case EntityActionType.Deleted:
                        {
                            listenerType = new CodeTypeReference(typeof(EventHandler<DeleteEventArgs>));
                            argumentType = new CodeTypeReference(typeof(DeleteEventArgs));
                            break;
                        }
                    case EntityActionType.Deleting:
                        {
                            listenerType = new CodeTypeReference(typeof(EventHandler<DeleteEventArgs>));
                            argumentType = new CodeTypeReference(typeof(DeleteEventArgs));
                            break;
                        } 
                    #endregion
                    #region Never, Validated, Validating, Authorized, Authorizing, Accessing, Accessed
                    case EntityActionType.Never:
                    case EntityActionType.Validated:
                    case EntityActionType.Validating:
                    case EntityActionType.Authorized:
                    case EntityActionType.Authorizing:
                    case EntityActionType.Accessing:
                    case EntityActionType.Accessed:
                        {
                            listenerType = new CodeTypeReference(typeof(EventHandler));
                            argumentType = new CodeTypeReference(typeof(EventArgs));
                            break;
                        } 
                    #endregion
                    #region Property Changed
                    case EntityActionType.PropertyChanged:
                        {
                            listenerType = new CodeTypeReference(typeof(PropertyChangedEventHandler));
                            argumentType = new CodeTypeReference(typeof(PropertyChangedEventArgs));
                            break;
                        } 
                    #endregion
                    default: break;
                }                

                CodeAttachEventStatement attachEvent = null;

                if (a.Async && argumentType != null)
                {
                    //create the async_begin method
                    CodeMemberMethod asyncListener = new CodeMemberMethod();
                    asyncListener.Name = a.Name + a.When.ToString() + "Begin";
                    asyncListener.Parameters.Add(new CodeParameterDeclarationExpression(typeof(object), "sender"));
                    asyncListener.Parameters.Add(new CodeParameterDeclarationExpression(argumentType, "e"));
                    declaration.Members.Add(asyncListener);

                    //Create the async_end method
                    CodeMemberMethod asyncEnd = new CodeMemberMethod();
                    asyncEnd.Name = a.Name + a.When.ToString() + "End";
                    asyncEnd.Parameters.Add(new CodeParameterDeclarationExpression(typeof(IAsyncResult), "result"));
                    declaration.Members.Add(asyncEnd);

                    //Create the handler to the actual event
                    
                    CodeVariableDeclarationStatement handler = new CodeVariableDeclarationStatement(
                        listenerType,
                        "handler", 
                        new CodeDelegateCreateExpression(
                            listenerType,                                 
                            (a.On == null ? 
                                (CodeExpression)(new CodeThisReferenceExpression()) : 
                                (CodeExpression)(new CodeVariableReferenceExpression(a.On))),
                            a.Name));
                    asyncListener.Statements.Add(handler);

                    //create the BeginInvoke call
                    CodeMethodInvokeExpression handlerBeginInvoke = new CodeMethodInvokeExpression(
                        new CodeVariableReferenceExpression("handler"),
                        "BeginInvoke",
                        new CodeVariableReferenceExpression("sender"),
                        new CodeVariableReferenceExpression("e"),
                        new CodeMethodReferenceExpression(
                            new CodeThisReferenceExpression(),
                            asyncEnd.Name),
                        new CodeVariableReferenceExpression("handler"));
                    asyncListener.Statements.Add(handlerBeginInvoke);
                    
                    //cast the async state into the handler
                    CodeVariableDeclarationStatement handlerEnd = new CodeVariableDeclarationStatement(
                        listenerType,
                        "handler",
                        new CodeCastExpression(
                            listenerType,
                            new CodePropertyReferenceExpression(
                                new CodeVariableReferenceExpression("result"),
                                "AsyncState")));
                    asyncEnd.Statements.Add(handlerEnd);

                    CodeMethodInvokeExpression endInvoke = new CodeMethodInvokeExpression(
                        new CodeVariableReferenceExpression("handler"),
                        "EndInvoke",
                        new CodeVariableReferenceExpression("result"));
                    asyncEnd.Statements.Add(endInvoke);

                    //Attach the listener to the async method.
                    CodeDelegateCreateExpression listener = new CodeDelegateCreateExpression(
                        listenerType,
                        new CodeThisReferenceExpression(),
                        asyncListener.Name);

                    attachEvent = new CodeAttachEventStatement(
                         new CodeThisReferenceExpression(),
                         a.When.ToString(),
                         listener);
                }
                else
                {
                    //Attach the listener right to the method.
                    CodeDelegateCreateExpression listener = new CodeDelegateCreateExpression(
                    listenerType,
                    (a.On == null ? 
                        (CodeExpression)(new CodeThisReferenceExpression()) : 
                        (CodeExpression)(new CodeVariableReferenceExpression(a.On))),
                    a.Name);

                    attachEvent = new CodeAttachEventStatement(
                         new CodeThisReferenceExpression(),
                         a.When.ToString(),
                         listener);
                }
                initializeActions.Statements.Add(attachEvent);
            }

            int index = declaration.Members.Count - 1;
            declaration.Members[index].EndDirectives.Add(
                new CodeRegionDirective(CodeRegionMode.End, ""));            
        }
        #endregion

        #region IHasChildren
        //Type[] IHasChildren.GetChildTypes()
        //{
        //    return new Type[] { };
        //}
        protected virtual void CreateGetChildTypes(Entity entity, CodeTypeDeclaration declaration)
        {
            CodeMemberMethod getChildTypes = new CodeMemberMethod();
            getChildTypes.StartDirectives.Add(new CodeRegionDirective(CodeRegionMode.Start, "IHasChildren.GetChildTypes"));
            getChildTypes.EndDirectives.Add(new CodeRegionDirective(CodeRegionMode.End, ""));
            getChildTypes.Name = "GetChildTypes";
            getChildTypes.Attributes = MemberAttributes.Public | MemberAttributes.Override;
            getChildTypes.ReturnType = new CodeTypeReference(typeof(Type[]));
            
            List<CodeTypeOfExpression> children = new List<CodeTypeOfExpression>();
            foreach(EntityRelationship r in entity.Relationships)
            {
                if(r.Type == EntityRelationshipType.Child && r.From == entity)
                {
                    IChildRelationship childRelationship = Dependency.GetContract<IChildRelationship>(r.With);
                    if(childRelationship != null)
                    {
                        children.Add(childRelationship.TypeOfChild(r.With));
                    }
                }
            }

            CodeArrayCreateExpression childTypes = new CodeArrayCreateExpression(
                typeof(Type),
                children.ToArray());

            CodeMethodReturnStatement ret = new CodeMethodReturnStatement(childTypes);
            getChildTypes.Statements.Add(ret);

            declaration.Members.Add(getChildTypes);
        }
        #endregion

        #region Generate object overrides
        public virtual void GenerateObjectOverrides(Entity entity, CodeTypeDeclaration declaration)
        {
            CodeMemberMethod equals = GenerateEqualsOverride(entity);
            CodeMemberMethod getHashCode = GenerateGetHashCodeOverride(entity);
            CodeMemberMethod toString = GenerateToStringOverride(entity);

            equals.StartDirectives.Add(new CodeRegionDirective(CodeRegionMode.Start, "Object overrides"));
            toString.EndDirectives.Add(new CodeRegionDirective(CodeRegionMode.End, ""));

            declaration.Members.Add(equals);
            declaration.Members.Add(getHashCode);
            declaration.Members.Add(toString);
        }
        public virtual CodeMemberMethod GenerateEqualsOverride(Entity entity)
        {
            CodeMemberMethod equals = new CodeMemberMethod();
            equals.Name = "Equals";
            equals.Attributes = MemberAttributes.Public | MemberAttributes.Override;
            equals.ReturnType = new CodeTypeReference(typeof(bool));
            equals.Parameters.Add(new CodeParameterDeclarationExpression(typeof(object), "obj"));

            CodeVariableReferenceExpression obj = new CodeVariableReferenceExpression("obj");

            CodeVariableDeclarationStatement equal = new CodeVariableDeclarationStatement(
                typeof(bool),
                "equal",
                new CodePrimitiveExpression(false));


            CodeVariableReferenceExpression equalReference = new CodeVariableReferenceExpression("equal");

            CodeAssignStatement equalAssign = new CodeAssignStatement(
                equalReference, null);

            CodeConditionStatement ifGetType = new CodeConditionStatement(
                new CodeBinaryOperatorExpression(
                    new CodeBinaryOperatorExpression(
                        obj,
                        CodeBinaryOperatorType.IdentityInequality,
                        new CodePrimitiveExpression(null)),
                    CodeBinaryOperatorType.BooleanAnd,
                    new CodeMethodInvokeExpression(
                        new CodeTypeOfExpression(
                            new CodeTypeReference(entity.Name)),
                        "IsAssignableFrom",
                        new CodeMethodInvokeExpression(
                            obj,
                            "GetType"))),
                equalAssign);

            foreach (EntityField field in entity.Fields)
            {
                if (field.IsId)
                {
                     CodeBinaryOperatorExpression isIdEqual = new CodeBinaryOperatorExpression(
                            new CodePropertyReferenceExpression(
                                new CodeThisReferenceExpression(),
                                field.Name),
                            CodeBinaryOperatorType.ValueEquality,
                            new CodePropertyReferenceExpression(
                                new CodeCastExpression(
                                    new CodeTypeReference(entity.Name),
                                    obj),
                                field.Name));
                    if (equalAssign.Right == null)
                    {
                        equalAssign.Right = isIdEqual;
                    }
                    else
                    {
                        equalAssign.Right = new CodeBinaryOperatorExpression(
                            equalAssign.Right,
                            CodeBinaryOperatorType.ValueEquality,
                            isIdEqual);
                    }
                }
            }

            CodeMethodReturnStatement returnEqual = new CodeMethodReturnStatement(
                equalReference);

            equals.Statements.Add(equal);
            equals.Statements.Add(ifGetType);
            equals.Statements.Add(returnEqual);
            return equals;
        }
        public virtual CodeMemberMethod GenerateGetHashCodeOverride(Entity entity)
        {
            CodeMemberMethod getHashCode = new CodeMemberMethod();
            getHashCode.Name = "GetHashCode";
            getHashCode.Attributes = MemberAttributes.Public | MemberAttributes.Override;
            getHashCode.ReturnType = new CodeTypeReference(typeof(int));

            CodeMethodReturnStatement returnHashCode = new CodeMethodReturnStatement(null);

            foreach (EntityField field in entity.Fields)
            {
                if (field.IsId)
                {
                    CodeMethodInvokeExpression idGetHashCode = new CodeMethodInvokeExpression(
                        new CodePropertyReferenceExpression(
                            new CodeThisReferenceExpression(),
                            field.Name),
                        "GetHashCode");

                    if (returnHashCode.Expression == null)
                    {
                        returnHashCode.Expression = idGetHashCode;
                    }
                    else
                    {
                        returnHashCode.Expression = new CodeBinaryOperatorExpression(
                            returnHashCode.Expression,
                            CodeBinaryOperatorType.Multiply,
                            idGetHashCode);
                    }
                }
            }

            getHashCode.Statements.Add(returnHashCode);
            return getHashCode;
        }
        public virtual CodeMemberMethod GenerateToStringOverride(Entity entity)
        {
            CodeMemberMethod toStringMethod = new CodeMemberMethod();
            toStringMethod.Name = "ToString";
            toStringMethod.Attributes = MemberAttributes.Public | MemberAttributes.Override;
            toStringMethod.ReturnType = new CodeTypeReference(typeof(string));

            CodeMethodReturnStatement returnToString = new CodeMethodReturnStatement(null);

            foreach (EntityField field in entity.Fields)
            {
                if (field.IsId)
                {
                    CodeMethodInvokeExpression idToString = new CodeMethodInvokeExpression(
                        new CodePropertyReferenceExpression(
                            new CodeThisReferenceExpression(),
                            field.Name),
                        "ToString");

                    if (returnToString.Expression == null)
                    {
                        returnToString.Expression = idToString;
                    }
                    else
                    {
                        returnToString.Expression = new CodeBinaryOperatorExpression(
                            returnToString.Expression,
                            CodeBinaryOperatorType.Add,
                            new CodeBinaryOperatorExpression(
                                new CodePrimitiveExpression(", "),
                                CodeBinaryOperatorType.Add,
                                idToString));
                    }
                }
            }

            toStringMethod.Statements.Add(returnToString);
            return toStringMethod;
        }
        #endregion

        #region Generate operator overloads
        /// <summary>
        /// Is this even possible? It seems like it's not.
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="declaration"></param>
        public virtual void GenerateOperatorOverloads(Entity entity, CodeTypeDeclaration declaration)
        {
            CodeMemberMethod equalsOperator = GenerateEqualsOperator(entity);
            CodeMemberMethod notEqualsOperator = GenerateNotEqualsOperator(entity);

            equalsOperator.StartDirectives.Add(new CodeRegionDirective(CodeRegionMode.Start, "Operator overloads"));
            notEqualsOperator.EndDirectives.Add(new CodeRegionDirective(CodeRegionMode.End, ""));

            declaration.Members.Add(equalsOperator);
            declaration.Members.Add(notEqualsOperator);
        }
        public virtual CodeMemberMethod GenerateEqualsOperator(Entity entity)
        {
            CodeMemberMethod method = new CodeMemberMethod();
            method.Attributes = MemberAttributes.Static | MemberAttributes.Public;
            
            return null;
        }

        public virtual CodeMemberMethod GenerateNotEqualsOperator(Entity entity)
        {
            return null;
        }

        #endregion
    }
}
